问题描述
,我试图单元测试功能,使多次调用$ HTTP。
Using AngularJS, I am trying to unit test a function that makes multiple calls to $http.
我的测试看起来是这样的:
My test looks something like this:
it('traverses over a hierarchical structure over multiple chained calls', function() {
myService.traverseTheStuff()
.then(function(theAggregateResult) {
// ...is never fulfilled
});
$httpBackend.flush();
});
其他单通话测试将注册传给。那么(回调),一旦我把.flush(执行它)。
Other single-call tests will register the callback passed to .then() and execute it as soon as I call .flush().
被测code看起来是这样的。
The code under test looks something like this.
function traverseTheStuff(){
// This will make a call to $http to fetch some data
return getRootData()
// It is fulfilled at the end of the test when I $httpBackend.flush()
.then(function(rootData){
// Another call to $http happens AFTER $httpBackend.flush()
return getNextLevel(rootData.someReference);
})
// The second promise is never fulfilled and the test fails
.then(function(nextLevel){
return aggregateTheStuff(...);
});
}
有关什么它的价值,每个单个调用的是单位分别进行测试。在这里,我要遍历一棵树,汇总了一些数据和单元测试),该承诺链接正确挂接和b)聚合是准确的。拼合出来为单独分立的呼叫已经完成。
For what its worth, each of the single calls is unit tested separately. Here, I want to traverse a tree, aggregate some data and unit test a) that the promise chaining is hooked up correctly and b) the aggregation is accurate. Flattening it out into separate discrete calls is already done.
推荐答案
我在测试角初学者,但我设置测试一个非常类似的设置到你与sucessfull第二次,那么/许一个plnkr呼叫
I'm a beginner in testing Angular, but I've setup a plnkr that tests a very similar setup to yours with a sucessfull "second" then/promise call
<一个href=\"http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=$p$pview\">http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=$p$pview
下面code片段稍微简化了上述plnkr版本。
The below code snippets are slightly simplified versions of the above plnkr.
我发现的关键点是
-
我注意到功能traverseTheStuff根本不调用$ HTTP / $ httpBackend。它仅使用在$ Q承诺定义的函数,所以在测试假定使用$ Q的,并注入了
I note the function traverseTheStuff doesn't call $http/$httpBackend at all. It only uses functions defined in $q promises, so the testing on assumes use of $q, and injects that
var deferred1 = null;
var deferred2 = null;
var $q = null;
beforeEach(function() {
inject(function(_$q_) {
$q = _$q_;
});
});
beforeEach(function() {
deferred1 = $q.defer();
deferred2 = $q.defer();
}
要异步调用
的功能是窥探/存根以自己的承诺的回报值,其中在测试本身产生的诺言,所以测试时traverseTheStuff其实际执行不叫
The functions to be called asynchronously are spied/stubbed with their promise return values, where the promise is created in the test itself, so their actual implementation isn't called when testing traverseTheStuff
spyOn(MyService,'traverseTheStuff').andCallThrough();
spyOn(MyService,'getRootData').andReturn(deferred1.promise);
spyOn(MyService,'getNextLevel').andReturn(deferred2.promise);
spyOn(MyService,'aggregateTheStuff');
有没有任何呼叫,然后在测试中,只在测试中创建的承诺,其次是$ rootScope解析。$适用(),然后到实际调用,那么在traverseTheStuff,我们还可以测试被称为回调
There aren't any calls to "then" in the test, only to "resolve" on the promises created in the test, followed by $rootScope.$apply(), to then actually call the "then" callbacks in traverseTheStuff, which we can also test are called
beforeEach(function() {
spyOn(deferred1.promise, 'then').andCallThrough();
});
beforeEach(function() {
deferred1.resolve(testData);
$rootScope.$apply(); // Forces $q.promise then callbacks to be called
});
it('should call the then function of the promise1', function () {
expect(deferred1.promise.then).toHaveBeenCalled();
});
每个承诺必须得到解决/ $适用-ED调用链中的下一个,然后功能。所以。让测试调用aggregateTheStuff(或者更确切地说,它的存根),第二个承诺,从getNextLevel存根返回,还必须解决:
Each promise must be resolved/$apply-ed to call the next "then" function in the chain. So. to get the test to call aggregateTheStuff (or rather, its stub), the second promise, returned from the getNextLevel stub, must also be resolved:
beforeEach(function() {
deferred2.resolve(testLevel);
$rootScope.$apply(); // Forces $q.promise then callbacks to be called
});
it('should call aggregateTheStuff with ' + testLevel, function () {
expect(MyService.aggregateTheStuff).toHaveBeenCalledWith(testLevel);
});
上述所有的问题,在于它假设从$ q和$ rootScope某种行为。我下的理解单元测试像这不应该使这一假设,才能真正唯一的测试code中的一位。我还没有制定出如何解决这个问题,如果我误解。
An issue with all of the above, is that it assumes certain behaviour from $q and $rootScope. I was under the understanding unit tests like this shouldn't make this assumptions, in order to truly only test one bit of code. I've not worked out how to get around this, or if I'm misunderstanding.
这篇关于使用$如何单元测试一个angularjs承诺链httpBackend的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!