Javascript Angular和Jasmine:如何模拟返回承诺的服务

Javascript Angular和Jasmine:如何模拟返回承诺的服务,javascript,angularjs,unit-testing,mocking,jasmine,Javascript,Angularjs,Unit Testing,Mocking,Jasmine,我有以下AngularJS中的控制器,它从AngularMaterial调用“$mdSidenav”提供的服务。这是通过工厂方法提供的服务 angular.module('leopDirective', []) .controller('MenuCtrl', function ($scope,$timeout,$mdSidenav,$log) { $scope.mdSidenav = $mdSidenav; $scope.close = function () {

我有以下AngularJS中的控制器,它从AngularMaterial调用“$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();
    });
});
我遇到的问题是,我不知道如何为mock创建一个返回承诺的函数:

  • 创建承诺取决于$q
  • $q必须从块“inject”中注入
  • 但是,$PROFECT必须在“模块”中使用,就在“注入”之前
  • 因此,在我的mock对象(mock_umdsidenav().close())中返回承诺的函数在调用“$provide”之前必须是空的,并且在以后以某种方式创建
  • 使用这种方法,我得到的错误是以下错误(请参见Demo),它告诉我先创建空函数,然后再填充它是不起作用的:

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

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

    首先,使模拟服务方法返回承诺对象:

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

    演示:

    非常感谢您的帮助。我仍然无法理解的是,如果尚未注入模拟变量,那么在创建模拟变量时如何使用$q……好吧,从技术上讲,您是正确的,这有点令人困惑。最好在定义
    $q
    之后的每个块之前在
    中创建模拟服务。但是,在您的情况下,当创建
    mock_uumdsidenav
    时,不使用
    $q
    ,仅当调用
    close
    时才使用它。当它发生时,
    $q
    已经定义好了。好吧,这里的关键是要理解,当你在变量的定义中使用$q时,它的分辨率是“惰性的”。这个假设正确吗?