我有困难的时候试图Angularjs测试基于承诺的代码。
我在我的控制器下面的代码:
$scope.markAsDone = function(taskId) {
tasksService.removeAndGetNext(taskId).then(function(nextTask) {
goTo(nextTask);
})
};
function goTo(nextTask) {
$location.path(...);
}
我想单元测试以下情况:
- 当
markAsDone
被称为它应该调用tasksService.removeAndGetNext
- 当
tasksService.removeAndGetNext
做它应该改变位置(调用goTo
)
在我看来,有单独测试这两个例无简便的方法。
我所做的测试,第一次是:
var noopPromise= {then: function() {}}
spyOn(tasksService, 'removeAndGetNext').andReturn(noopPromise);
现在来测试第二种情况下我需要创建另一个虚假的承诺,将永远resolved
。 这一切都相当乏味,这是一个很大的样板代码。
是否有任何其他的方式来测试这样的事情? 抑或是我的设计嗅觉?
您仍需要模拟服务,并返回一个承诺,但你应该用真正的承诺来代替,这样你就不会需要实现其功能。 使用beforeEach
创建已被兑现承诺,并嘲笑服务,如果你需要它总是被解决。
var $rootScope;
beforeEach(inject(function(_$rootScope_, $q) {
$rootScope = _$rootScope_;
var deferred = $q.defer();
deferred.resolve('somevalue'); // always resolved, you can do it from your spec
// jasmine 2.0
spyOn(tasksService, 'removeAndGetNext').and.returnValue(deferred.promise);
// jasmine 1.3
//spyOn(tasksService, 'removeAndGetNext').andReturn(deferred.promise);
}));
如果你宁愿喜欢解决它在每it
用不同的值块,那么你只露出推迟到一个局部变量,并在规范解决。
当然,你会保持你的测试,因为他们,但这里是一些非常简单的规格向您展示它是如何工作的。
it ('should test receive the fulfilled promise', function() {
var result;
tasksService.removeAndGetNext().then(function(returnFromPromise) {
result = returnFromPromise;
});
$rootScope.$apply(); // promises are resolved/dispatched only on next $digest cycle
expect(result).toBe('somevalue');
});
另一种方法是以下,取直出控制器我的测试:
var create_mock_promise_resolves = function (data) {
return { then: function (resolve) { return resolve(data); };
};
var create_mock_promise_rejects = function (data) {
return { then: function (resolve, reject) { if (typeof reject === 'function') { return resolve(data); } };
};
var create_noop_promise = function (data) {
return { then: function () { return this; } };
};
而作为另一种选择,你可不必调用$digest
通过使用Q库( https://github.com/kriskowal/q )作为一个下拉更换为$q
例如:
beforeEach(function () {
module('Module', function ($provide) {
$provide.value('$q', Q);
});
});
这样的承诺可以解决/拒绝$消化周期之外。