【问题标题】:Unit test service that returns promise Angularjs Jasmine返回承诺 Angularjs Jasmine 的单元测试服务
【发布时间】:2014-05-19 07:01:11
【问题描述】:

根据 Michal Charemza 帖子编辑。

我有一个代表 angularui 模态对话框的服务:

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
    };

});

如果用户从对话框中单击了“确定”,则调用删除方法requestNotificationChannel.deleteMessage(id) 被执行。

$scope.deleteMessage = function(id) {
        var result = dialogFactory.confirmDeleteDialog();

        result.then(function(response) {
            requestNotificationChannel.deleteMessage(id);
        });
    };

问题是我无法对此进行单元测试。

这是我的测试。我已经正确注入了 q 服务,但我不确定我应该从"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();
            });
        });

但我收到了expected spy deleteMessage to have been called。这意味着 result.then... 部分未执行。我错过了什么?

【问题讨论】:

    标签: angularjs jasmine promise


    【解决方案1】:

    要模拟一个返回 Promise 的函数,它还需要返回一个 Promise,然后需要将其作为一个单独的步骤来解决。

    在您的情况下,您传递给间谍的deferred.resolve() 需要替换为deferred.promise,并且 deferred.resolve() 单独执行。

    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.$digest(),因为 Angular 的 promise 实现与摘要循环相关。

    另外,与您的问题稍微无关,但我认为您不需要在 confirmDeleteDialog 中创建自己的延迟对象。您使用的(反)模式已被标记为“被遗忘的承诺”,如 http://taoofcode.net/promise-anti-patterns/

    如果更简单,使用更少的代码,并且我认为可以更好地处理错误,您可以返回 $modal 服务创建的承诺:

    var modalInstance = $modal.open({...});
    return modalInstance.result;
    

    如果你想修改调用函数看到的内容,就解析/拒绝值而言,你可以通过返回then的结果来创建一个链式promise:

    var modalInstance = $modal.open({...});
    return modalInstance.result.then(function(successResult) {
      return 'My other success result';
    }, function(failureReason) {
      return $q.reject('My other failure reason');
    });
    

    如果您不想将函数的内部工作原理暴露给它的调用者,您通常会想要这样做。这类似于同步编程中重新抛出异常的概念。

    【讨论】:

    • 我添加了关于$rootScope.$digest()的部分
    • 是的,这里肯定需要$digest()
    猜你喜欢
    • 2014-07-05
    • 2014-06-17
    • 2016-06-17
    • 2017-08-02
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 2014-05-14
    • 1970-01-01
    相关资源
    最近更新 更多