使用 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) 聚合是否准确。将其扁平化为单独的离散调用已经完成。
我是测试 Angular 的初学者,但我已经设置了一个 plunker,它通过成功的“第二次”then/promise 调用来测试与您非常相似的设置
http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=preview http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=preview
下面的代码片段是上面 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();
}
-
要异步调用的函数通过其 Promise 返回值进行监视/存根,其中 Promise 是在测试本身中创建的,因此在测试 traverseTheStuff 时不会调用它们的实际实现
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();
});
-
每个 Promise 必须被解析/$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 的某些行为。我理解这样的单元测试不应该做出这样的假设,以便真正只测试一点代码。我还没有弄清楚如何解决这个问题,或者我是否误解了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)