单元测试服务,返回的承诺Angularjs茉莉花

单元测试服务,返回的承诺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.