我使用ng-describe
编写了以下jasmine测试,并使用karma
运行。
(我正在使用es6-promise polyfill for PhantomJS)
var myModule = angular.module('MyModule', []);
myModule.service('MyService', [function() {
return {
getItems: function() {
// this will be spied and mocked
}
};
}]);
myModule.controller('MyController', ['$scope', 'MyService',
function($scope, MyService) {
$scope.items = [];
$scope.refreshItems = function() {
MyService.getItems().then(
function ok(items) {
$scope.items = items;
console.log('OK, items.length = ' + items.length);
},
function fail(reason) {
console.error('FAIL')
}).catch(console.error.bind(console));
};
}
]);
ngDescribe({
name: "MyController test",
modules: ['MyModule'],
inject: ['MyService', '$rootScope', '$q'],
controllers: 'MyController',
tests: function(deps) {
function getPromise(val) {
return new Promise(function(resolve, reject) {
resolve(val);
});
}
it('updates $scope.items', function() {
spyOn(deps.MyService, 'getItems').and.returnValue(getPromise([4, 5, 6]));
deps.MyController.refreshItems();
deps.$rootScope.$digest();
expect(deps.MyService.getItems).toHaveBeenCalled();
expect(deps.MyController.items.length).toBe(3);
console.log("END OF TEST");
});
}
});
测试会失败,因为承诺得到解决太晚了:
LOG: 'END OF TEST'
PhantomJS 1.9.8 (Windows 7 0.0.0) MyController test updates $scope.items FAILED
Expected 0 to be 3.
at d:/git/myproject/test/controllers/ItmngtControllerTest.js:49
LOG: 'OK, items.length = 3'
PhantomJS 1.9.8 (Windows 7 0.0.0): Executed 37 of 37 (1 FAILED) (0.085 secs / 0.26 secs)
经过长时间的调查后,我发现如果我使用$q
代替Promise
,它会正常工作。
function getPromise(val) {
var deferred = deps.$q.defer();
deferred.resolve(val);
return deferred.promise;
}
我想知道,为什么会出现这种情况,我可以在测试中更改某些内容以使用Promise
代替$q
来使测试通过吗?
我在不同的地方读过有关$rootScope.$apply()
的内容,但无论我把它放在哪里,它仍然不适用于我。
答案 0 :(得分:2)
Angular $q
测试是同步的,这是一个巨大的优势。调用范围$digest()
后,可以预期所有$q
承诺链处理程序也会被调用。
另一方面,一般的承诺(包括ES6实现)在设计上是异步的。一旦承诺得到解决,它的处理程序将在下一个滴答中被调用。如此令人愉快的Angular测试不再是那种同步:
it('updates $scope.items', function (done) {
...
setTimeout(() => {
expect(deps.MyService.getItems).toHaveBeenCalled();
expect(deps.MyController.items.length).toBe(3);
console.log("END OF TEST");
done();
});
});
可以将 Promise
模拟为同步测试目的,在这种情况下可以使用mock-promises
。
还有jasmine-co
旨在使用ES6生成器和co
更容易(实际上)在Jasmine中进行异步测试,我不知道它对{的执行情况有多好{1}}。