Javascript httpBackend模拟AJAX ES6承诺,单位为$q
我试图模拟对JSONP GET请求的响应,该请求是由一个返回ES6承诺的函数发出的,我在Javascript httpBackend模拟AJAX ES6承诺,单位为$q,javascript,angularjs,ajax,unit-testing,jasmine,Javascript,Angularjs,Ajax,Unit Testing,Jasmine,我试图模拟对JSONP GET请求的响应,该请求是由一个返回ES6承诺的函数发出的,我在$q.when()中包装了该承诺。代码本身工作得很好,但是,在单元测试中,$httpBackend并没有捕获请求,而是直接到达实际的URL。因此,当调用flush()时,我会收到一个错误,说明错误:没有挂起的刷新请求。JSONP请求是通过ES6承诺中jQuery的$.getJSON()发出的,因此我选择通过提供正则表达式而不是硬编码URL来捕获所有传出请求 我已经搜索了一段时间,试图找出这一点,但仍然没有理解
$q.when()
中包装了该承诺。代码本身工作得很好,但是,在单元测试中,$httpBackend并没有捕获请求,而是直接到达实际的URL。因此,当调用flush()
时,我会收到一个错误,说明错误:没有挂起的刷新请求代码>。JSONP请求是通过ES6承诺中jQuery的$.getJSON()
发出的,因此我选择通过提供正则表达式而不是硬编码URL来捕获所有传出请求
我已经搜索了一段时间,试图找出这一点,但仍然没有理解是什么原因导致电话通过。我觉得ES6承诺中的HTTP请求是“在Angular之外”发出的,因此$httpBackend不知道/无法捕获它,尽管如果调用是在开始时的$q承诺内发出的,则情况可能并非如此。有没有人能告诉我为什么要打这个电话,为什么一个简单的超时就可以了?我在这里尝试了$scope.$apply
、$scope.$digest
和$httpBackend.flush()
的所有组合,但都没有成功
也许一些代码可以更好地解释它
控制器
function homeController() {
...
var self = this;
self.getData = function getData() {
$q.when(user.getUserInformation()).then(function() {
self.username = user.username;
});
};
}
单元测试
...
beforeEach(module('home'));
describe('Controller', function() {
var $httpBackend, scope, ctrl;
beforeEach(inject(function(_$httpBackend_, $rootScope, $componentController) {
$httpBackend = _$httpBackend_;
scope = $rootScope.$new(); // used to try and call $digest or $apply
// have also tried whenGET, when('GET', ..), etc...
$httpBackend.whenJSONP(/.*/)
.respond([
{
"user_information": {
"username": "TestUser",
}
}
]);
ctrl = $componentController("home");
}));
it("should add the username to the controller", function() {
ctrl.getData(); // make HTTP request
$httpBackend.flush(); // Error: No pending request to flush !
expect(ctrl.username).toBe("TestUser");
});
});
...
然而,出于某些原因,这是可行的:
it("should add the username to the controller", function() {
ctrl.getData(); // make HTTP request
setTimeout(() => {
// don't even need to call flush, $digest, or $apply...?
expect(ctrl.username).toBe("TestUser");
});
});
多亏了格雷厄姆的评论,我被带进了另一个兔子洞,因为我不了解一些事情,我将在这里总结一下,以防有人在同样的情况下结束
我没有完全理解JSONP是如何工作的。它根本不依赖XmlHttpRequest(请参阅)。我没有试图通过JSONP来处理对这些请求的模拟响应,而是简单地切换了我正在使用的代码上的“debug”标志,该标志禁用了JSONP,因此调用是通过XHR对象进行的(如果需要来自此外部API的实际响应,这将失败)
我没有尝试使用jasmine ajax,而是在jQuery的getJSON上设置了一个间谍,并返回了一个模拟响应。这最终将模拟响应发送到ES6承诺,但由于某种原因,$q承诺对象的then
函数(包装ES6承诺后生成)没有被调用(也没有调用任何其他错误处理函数,甚至finally
)。我还尝试在几乎任何地方调用$scope.$apply()
,这可能会有所帮助,但没有用
基本实现(单元测试):
...
spyOn($, 'getJSON').and.callFake(function (url, success) {
success({"username": "TestUser"}); // send mock data
});
ctrl.getData(); // make GET request
...
// user.getUserInformation() returns an ES6 promise
$q.when(user.getUserInformation()).then(function() {
// this was never being called / reached! (in the unit tests)
});
...
ctrl.getData(); // make GET request
setTimeout(() => {
expect(ctrl.username).toBe("TestUser"); // works!
});
问题(控制器源中):
...
spyOn($, 'getJSON').and.callFake(function (url, success) {
success({"username": "TestUser"}); // send mock data
});
ctrl.getData(); // make GET request
...
// user.getUserInformation() returns an ES6 promise
$q.when(user.getUserInformation()).then(function() {
// this was never being called / reached! (in the unit tests)
});
...
ctrl.getData(); // make GET request
setTimeout(() => {
expect(ctrl.username).toBe("TestUser"); // works!
});
最终,我使用了#2的实现来发送数据,并将断言封装在单元测试中的一个超时内,没有指定持续时间。我意识到这不是最好的,希望这不是应该怎么做的,但经过几个小时的努力,我已经达到了极限,放弃了。如果有人对如何改进这一点有任何想法,或者为什么没有被调用,我真的很想听听
单元测试:
...
spyOn($, 'getJSON').and.callFake(function (url, success) {
success({"username": "TestUser"}); // send mock data
});
ctrl.getData(); // make GET request
...
// user.getUserInformation() returns an ES6 promise
$q.when(user.getUserInformation()).then(function() {
// this was never being called / reached! (in the unit tests)
});
...
ctrl.getData(); // make GET request
setTimeout(() => {
expect(ctrl.username).toBe("TestUser"); // works!
});
httpBackend是一个模拟实现,它由angular注入$http服务进行单元测试,如果您不使用$http服务发出请求,您将无法使用httpBackend捕获它