使用AngularJS,我试图对一个多次调用$ http的函数进行单元测试。
我的测试看起来像这样:
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)聚合是准确的。将其展平为单独的离散调用已经完成。
答案 0 :(得分:18)
我是测试Angular的初学者,但是我已经设置了一个plnkr,用一个成功的“秒”然后/保证呼叫来测试与你的设置非常相似的设置
http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=preview
以下代码片段是上述plnkr的略微简化版本。
我发现的关键点是
我注意到函数traverseTheStuff根本没有调用$ http / $ httpBackend。它只使用$ q promises中定义的函数,因此测试假定使用$ 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();
}
异步调用的函数使用其promise返回值进行间谍/存根,其中promise在测试本身中创建,因此在测试traverseTheStuff时不会调用它们的实际实现
spyOn(MyService,'traverseTheStuff').andCallThrough();
spyOn(MyService,'getRootData').andReturn(deferred1.promise);
spyOn(MyService,'getNextLevel').andReturn(deferred2.promise);
spyOn(MyService,'aggregateTheStuff');
在测试中没有任何“then”的调用,只是为了“解析”测试中创建的promises,然后是$ rootScope。$ apply(),然后实际调用“then” “traverseTheStuff中的回调,我们也可以测试它们被称为
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存根返回的第二个promise:
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的某些行为。我是在理解单元测试这样的假设不应该做出这样的假设,以便真正只测试一点代码。我没有弄清楚如何解决这个问题,或者我是否误解了。