【问题标题】:Angular and Jasmine: how to mock a service that returns a promiseAngular 和 Jasmine:如何模拟返回承诺的服务
【发布时间】:2015-07-12 08:34:57
【问题描述】:

我在 AngularJS 中有以下控制器,它从 Angular Material 调用“$mdSidenav”提供的服务。这是由工厂方法提供的服务。

angular.module('leopDirective', [])
    .controller('MenuCtrl', function ($scope,$timeout,$mdSidenav,$log) {

    $scope.mdSidenav = $mdSidenav;
    $scope.close = function () {
        $mdSidenav('left').close().then(function() {$scope.closed=true; });
    };

});

我正在尝试模拟该服务 ($mdSidenav) 以测试我的控制器。以下是我试图定义的 Jasmine 测试的源代码:

describe('Controller: MenuCtrl', function() {
    var $rootScope, $scope, $controller, $q, menuCtrl,
        mock__mdSidenav = function(component) {
            return {
                // THIS FUNCTION IS EMPTY SINCE $Q IS NOT AVAILABLE
                close: function() {}
            }
        };

    beforeEach(function() {
        module('leopDirective', function($provide) {
            // I HAVE TO PROVIDE THE MOCK UP BEFORE INJECTING $Q
            $provide.value('$mdSidenav', mock__mdSidenav);
        });
        inject(function($injector) {
            $rootScope = $injector.get('$rootScope');
            $controller = $injector.get('$controller');
            $q = $injector.get('$q');
            $scope = $rootScope.$new();
        });
        menuCtrl = $controller("MenuCtrl", { $scope: $scope });
    });

    it('should create the $mdSidenav object', function() {
        var deferred = $q.defer(),
            promise = deferred.promise;
        // NOW THAT $Q IS AVAILABLE, I TRY TO FILL UP THE FUNCTION
        mock__mdSidenav.close = function() {
            return promise.then(function(response){return response.success;});
        };
        // force `$digest` to resolve/reject deferreds
        $rootScope.$digest();
        // INVOKE FUNCTION TO TEST
        $scope.close();
    });
});

我的问题是我不知道如何为返回承诺的模拟创建一个函数:

  1. 创建承诺取决于 $q,
  2. $q 必须从“inject”块中注入,
  3. 但是,$provide 必须在“模块”中使用,就在“注入”之前,
  4. 因此,在我的模拟对象 (mock__mdSidenav().close()) 中返回承诺的函数在调用“$provide”之前必须为空,并且以某种方式稍后创建。

使用这种方法,我得到的错误是以下错误(参见 Demo plnkr),它告诉我先创建空函数然后再填充它不起作用:

TypeError: Cannot read property 'then' of undefined
    at Scope.$scope.close (app.js:7:35)

模拟具有返回承诺的函数的服务的正确方法是什么?

【问题讨论】:

    标签: javascript angularjs unit-testing mocking jasmine


    【解决方案1】:

    首先,让 mock 服务方法返回 Promise 对象:

    mock__mdSidenav = function(component) {
      return {
        isMock: true, 
        close: function() {
            return $q.when();
        }
      }
    };
    

    然后将其注入到控制器实例中:

    menuCtrl = $controller("MenuCtrl", { 
        $scope: $scope,
        $mdSidenav: mock__mdSidenav
    });
    

    最后,写一些期望:

    it('should create the $mdSidenav object', function() {
    
      $scope.close();
      $rootScope.$digest();
    
      expect($scope.closed).toBe(true);
    });
    

    演示: http://plnkr.co/edit/dr79GgZYG2tjiuWU0oFx?p=preview

    【讨论】:

    • 非常感谢您的帮助。我仍然无法理解的是,如果尚未注入模拟变量,如何在创建模拟变量时使用 $q...
    • 好吧,从技术上讲,您是正确的,这有点令人困惑。在定义$q 之后,最好在beforeEach 块中创建模拟服务。但是,在您的情况下,在创建 mock__mdSidenav 时不使用 $q ,仅在调用 close 时才使用它。当它发生时,$q 已经定义好了。
    • 好的,所以这里的关键是要了解,当您在该变量的定义中使用 $q 时,它的分辨率是“惰性的”。这个假设正确吗?
    猜你喜欢
    • 2015-05-29
    • 2014-07-05
    • 2017-06-03
    • 1970-01-01
    • 1970-01-01
    • 2016-06-17
    • 2014-05-19
    • 1970-01-01
    • 2017-02-21
    相关资源
    最近更新 更多