我使用 ng-describe
编写了以下 Jasmine 测试,并使用 karma
运行。
(我正在为 PhantomJS 使用 es6-promise polyfill)
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");
});
}
});
测试会失败,因为 promise 解决得太晚了:
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()
的内容,但无论我把它放在哪里,它仍然对我不起作用。
最佳答案
Angular $q
测试是同步的,这是一个巨大的优势。一旦调用作用域 $digest()
,就可以预期所有 $q
promise 链处理程序也会被调用。
另一方面,Promise 通常(包括 ES6 实现)在设计上是异步的。一旦 Promise 被解决,它的处理程序将在下一个tick 时被调用。令人愉快的 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 中的异步测试变得更容易(实际上确实如此),我不知道它在 ng-describe 中的性能如何。
关于angularjs - Promise 在 AngularJS Jasmine 测试中解决得太晚了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34228045/