单元测试服务,返回的承诺Angularjs茉莉花
每米哈尔Charemza后编辑。
EDITED per Michal Charemza post.
我有重新presents angularui模态对话框服务:
I have a service that represents angularui modal dialog:
app.factory("dialogFactory", function($modal, $window, $q) {
function confirmDeleteDialog() {
var modalInstance = $modal.open({
templateUrl: "../application/factories/confirmDeleteDialog.htm",
controller: function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close("true");
};
$scope.cancel = function() {
$modalInstance.dismiss("false");
};
}
});
return modalInstance.result.then(function(response) {
return 'My other success result';
}, function(response) {
return $q.reject('My other failure reason');
});
};
return {
confirmDeleteDialog: confirmDeleteDialog
};
});
在调用删除方法,如果用户点击OK从对话框 requestNotificationChannel.deleteMessage(ID)
执行
On calling the delete method if the user has clicked Ok from the dialog requestNotificationChannel.deleteMessage(id)
is executed.
$scope.deleteMessage = function(id) {
var result = dialogFactory.confirmDeleteDialog();
result.then(function(response) {
requestNotificationChannel.deleteMessage(id);
});
};
问题是我不能够单元测试这一点。
The problem is I am not able to unit test this.
这是我的测试。我已经正确地注入问答服务,但我不知道我应该从confirmDeleteDialog
间谍...
This is my test. I have correctly injected the q service but I am not sure what should I return from "confirmDeleteDialog"
spy...
describe("has a delete method that should call delete message notification", function() {
var deferred = $q.defer();
spyOn(dialogFactory, "confirmDeleteDialog").and.returnValue(deferred.promise);
spyOn(requestNotificationChannel, "deleteMessage");
$scope.deleteMessage(5);
deferred.resolve();
it("delete message notification is called", function() {
expect(requestNotificationChannel.deleteMessage).toHaveBeenCalled();
});
});
但是我收到预计间谍DeleteMessage可以到被称为
。这意味着,不执行 result.then
...的一部分。我缺少什么?
But I am receiving expected spy deleteMessage to have been called
. Which means that the result.then
... part is not executed. What am I missing ?
要嘲笑返回一个承诺一个功能,它需要还回一个承诺,然后需要解决作为单独的步骤。
To mock a function that returns a promise, it will need to also return a promise, which then needs to be resolved as a separate step.
在你的情况 deferred.resolve()
传递给间谍需要与 deferred.promise
被替换>和deferred.resolve()分别进行。
In your case the deferred.resolve()
you pass to the spy needs to be replaced with deferred.promise
, and the deferred.resolve() performed separately.
beforeEach(function() {
var deferred = $q.defer();
spyOn(dialogFactory, "confirmDeleteDialog").and.returnValue(deferred.promise);
spyOn(requestNotificationChannel, "deleteMessage");
$scope.deleteMessage(5);
deferred.resolve();
$rootScope.$digest();
});
it("delete message notification is called", function() {
expect(requestNotificationChannel.deleteMessage).toHaveBeenCalled();
});
我怀疑你还需要调用 $ rootScope。$摘要()
,如角的承诺的实施是联系在一起的消化循环。
I suspect you also need to call $rootScope.$digest()
, as Angular's promise implementation is tied to the digest loop.
此外,稍微无关的问题,但我不认为你需要创建 confirmDeleteDialog
自己递延对象。您正在使用的(反)模式已经被标记为被遗忘的承诺,如http://taoof$c$c.net/promise-anti-patterns/
Also, slightly unrelated to your question, but I don't think you need to create your own deferred object in confirmDeleteDialog
. The (anti) pattern you're using has been labelled 'The Forgotten Promise', as in http://taoofcode.net/promise-anti-patterns/
在更简单,使用更少的code,我认为,允许更好的错误处理,你可以只返回了 $模式
服务创建承诺:
When is simpler, uses less code, and I think that allows better error handling, you can just return the promise that the $modal
service creates:
var modalInstance = $modal.open({...});
return modalInstance.result;
如果你想修改调用函数所看到的,在解决/拒绝的价值观方面,你可以通过返回的结果,那么
创建一个链接承诺:
If you want to modify what the calling function sees, in terms of resolved/rejected values, you can create a chained promise by returning the result of then
:
var modalInstance = $modal.open({...});
return modalInstance.result.then(function(successResult) {
return 'My other success result';
}, function(failureReason) {
return $q.reject('My other failure reason');
});
您通常会想这样做,如果你不希望暴露一个函数的内部运作它的调用者。这类似于重新抛出异常的同步编程的概念。
You would usually want to do this if you don't want to expose the inner-workings of a function to its caller. This is analogous to the concept of re-throwing an exception in synchronous programming.