Unit testing 如何使用$httpBackend对angularjs承诺链进行单元测试
使用AngularJS,我试图对一个函数进行单元测试,该函数对$http进行多次调用 我的测试如下所示:Unit testing 如何使用$httpBackend对angularjs承诺链进行单元测试,unit-testing,angularjs,jasmine,promise,Unit Testing,Angularjs,Jasmine,Promise,使用AngularJS,我试图对一个函数进行单元测试,该函数对$http进行多次调用 我的测试如下所示: it('traverses over a hierarchical structure over multiple chained calls', function() { myService.traverseTheStuff() .then(function(theAggregateResult) { // ...is never fulfilled
it('traverses over a hierarchical structure over multiple chained calls', function() {
myService.traverseTheStuff()
.then(function(theAggregateResult) {
// ...is never fulfilled
});
$httpBackend.flush();
});
其他单次调用测试将注册传递给.then()的回调,并在调用.flush()后立即执行它
测试中的代码如下所示
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(...);
});
}
不管它的价值是什么,每个单独的调用都是单独进行单元测试的。在这里,我想遍历一棵树,聚合一些数据并进行单元测试a)承诺链接是否正确连接,b)聚合是否准确。将其扁平化为单独的离散调用已经完成。我是测试Angular的初学者,但我已经设置了一个plnkr,通过一个成功的“秒”then/PROMITE调用来测试与您非常相似的设置 下面的代码片段是上述plnkr的稍微简化版本 我发现的关键点是
- 我注意到函数traverseTheStuff根本不调用$http/$httpBackend。它只使用$q承诺中定义的函数,因此对的测试假设使用$q,并注入该函数
var deferred1 = null; var deferred2 = null; var $q = null; beforeEach(function() { inject(function(_$q_) { $q = _$q_; }); }); beforeEach(function() { deferred1 = $q.defer(); deferred2 = $q.defer(); }
- 异步调用的函数使用它们的承诺返回值进行监视/存根,承诺是在测试本身中创建的,因此在测试时不会调用它们的实际实现
spyOn(MyService,'traverseTheStuff').andCallThrough(); spyOn(MyService,'getRootData').andReturn(deferred1.promise); spyOn(MyService,'getNextLevel').andReturn(deferred2.promise); spyOn(MyService,'aggregateTheStuff');
- 在测试中没有任何对“then”的调用,只是对在测试中创建的承诺进行“解析”,后面跟着$rootScope.$apply(),然后实际调用traverseTheStuff中的“then”回调,我们也可以对其进行测试
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(); });
- 必须解析每个承诺/$apply ed以调用链中的下一个“then”函数。所以要让测试调用aggregateTheStuff(或者更确切地说,它的存根),还必须解析从getNextLevel存根返回的第二个承诺:
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中存在某些行为。我理解像这样的单元测试不应该做出这样的假设,以便真正只测试一位代码。我还没有想好如何避开这个问题,或者如果我有误解。非常感谢!这看起来很有希望。我还刚刚开始学习angular、jasmine和测试javascript。今晚我将阅读有关spyOn的文章,并消化你的解决方案。