Angularjs 承诺间谍

Angularjs 承诺间谍,angularjs,unit-testing,jasmine,karma-jasmine,Angularjs,Unit Testing,Jasmine,Karma Jasmine,我有以下功能,我想间谍。。。但它包含一个承诺。。。但是我得到了TypeError:“undefined”不是一个对象(计算“modalService.showmodel({},modalOptions.then”) 因为我当然只有spyOn(modalService,'showModal') 我怎么解释这个承诺呢 _modalService = { close: function (value) { console.log(value) }, dismiss: function

我有以下功能,我想间谍。。。但它包含一个承诺。。。但是我得到了TypeError:“undefined”不是一个对象(计算“modalService.showmodel({},modalOptions.then”)

因为我当然只有spyOn(modalService,'showModal')

我怎么解释这个承诺呢

_modalService = {
    close: function (value) { console.log(value) },
    dismiss: function (value) { console.log(value) },
    showModal: function (value) { console.log(value) }
};

spyOn(_modalService, 'close');
spyOn(_modalService, 'dismiss');
spyOn(_modalService, 'showModal');
控制器功能:

user.resetPassword = function () {
            var modalOptions = {
                closeButtonText: 'Cancel',
                actionButtonText: 'Reset',
                headerText: 'Reset Password',
                bodyText: 'Are you sure you want to reset the users password?'
            };

            modalService.showModal({}, modalOptions).then(function (result) {
                if (result === 'ok') {
                    userDataService.resetPassword(user.data).then(function (result) {
                        $scope.$emit('showSuccessReset');
                    });

                };
            });
        };
下面是我的单元测试:

it('should allow the users password to be reset', function () {
        var controller = createController();
        controller.resetPassword();
        $httpBackend.flush();
    })
*******************更新

所以我把它改为:

 //Create a fake instance of the modal instance. TO ensure that the close is called
        _modalService = {
            close: function (value) { console.log(value) },
            dismiss: function (value) { console.log(value) },
            showModal: function (value) { console.log(value) }
        };

        spyOn(_modalService, 'close');
        spyOn(_modalService, 'dismiss');
        spyOn(_modalService, 'showModal').and.callThrough();

        _modalService.showModal = function() {
                var deferred = $q.defer();
                deferred.resolve('Remote call result');
                return deferred.promise;   
            };

老实说,虽然我不确定我能不能解释这一点。虽然我了解所有异步的东西。。。我不知道jasmine是如何利用这一点来让一切顺利进行的。有人能解释一下流程吗????而且我觉得语法是错误的。。。您通常如何编写此代码以使其看起来更好/更干净…?

当您需要模拟返回承诺的函数时,您有两个选项:

  • 返回模拟的承诺(类似于承诺的对象)
  • 回报一个真正的承诺
  • 我建议#2,因为它更简单,而且您不必担心复制整个promise API。换句话说,不值得嘲笑承诺本身

    现在谈谈Jasmine:当你已经有了一个对象(不是模拟对象)并且你想监视它的一个方法(不是双关语)时,你只需要使用
    spyOn
    。在您的例子中,整个对象都是假的,因此您可以使用
    jasmine.createSpyObj

    以下示例应使上述内容更加清晰:

    SUT

    app.controller('MainCtrl', function($scope, modal, service) {
      $scope.click = function() {
        modal.show().then(function(result) {
          if (result === 'ok') {
            service.resetPassword();
          }
        });
      };
    });
    
    测试

    describe('Testing a controller', function() {
      var $scope, $q,
          ctrl, modalMock, serviceMock;
    
      beforeEach(function() {
        module('plunker');
    
        modalMock = jasmine.createSpyObj('modal', ['show']);
        serviceMock = jasmine.createSpyObj('service', ['resetPassword']);
    
        inject(function($rootScope, $controller, _$q_) {
          $scope = $rootScope.$new();
          $q = _$q_;
    
          ctrl = $controller('MainCtrl', {
            $scope: $scope,
            modal: modalMock,
            service: serviceMock
          });
        });
      });
    
      it('should reset the password when the user confirms', function() {
        // Arrange
        var deferred = $q.defer();
    
        deferred.resolve('ok');
        modalMock.show.and.returnValue(deferred.promise);
    
        // Act
        $scope.click();
        $scope.$digest(); // Makes Angular resolve the promise
    
        // Assert
        expect(serviceMock.resetPassword).toHaveBeenCalled();
      });
    
      it('should not reset the password when the user cancels', function() {
        // Arrange
        var deferred = $q.defer();
    
        deferred.resolve('cancel');
        modalMock.show.and.returnValue(deferred.promise);
    
        // Act
        $scope.click();
        $scope.$digest(); // Makes Angular resolve the promise
    
        // Assert
        expect(serviceMock.resetPassword).not.toHaveBeenCalled();
      });
    });
    


    每个测试中的模拟安排代码可以移动到每个之前的
    部分,这样它就不会重复。我这样做不是为了让事情变得简单。

    当您需要模拟返回承诺的函数时,您有两个选择:

  • 返回模拟的承诺(类似于承诺的对象)
  • 回报一个真正的承诺
  • 我建议#2,因为它更简单,而且您不必担心复制整个promise API。换句话说,不值得嘲笑承诺本身

    现在谈谈Jasmine:当你已经有了一个对象(不是模拟对象)并且你想监视它的一个方法(不是双关语)时,你只需要使用
    spyOn
    。在您的例子中,整个对象都是假的,因此您可以使用
    jasmine.createSpyObj

    以下示例应使上述内容更加清晰:

    SUT

    app.controller('MainCtrl', function($scope, modal, service) {
      $scope.click = function() {
        modal.show().then(function(result) {
          if (result === 'ok') {
            service.resetPassword();
          }
        });
      };
    });
    
    测试

    describe('Testing a controller', function() {
      var $scope, $q,
          ctrl, modalMock, serviceMock;
    
      beforeEach(function() {
        module('plunker');
    
        modalMock = jasmine.createSpyObj('modal', ['show']);
        serviceMock = jasmine.createSpyObj('service', ['resetPassword']);
    
        inject(function($rootScope, $controller, _$q_) {
          $scope = $rootScope.$new();
          $q = _$q_;
    
          ctrl = $controller('MainCtrl', {
            $scope: $scope,
            modal: modalMock,
            service: serviceMock
          });
        });
      });
    
      it('should reset the password when the user confirms', function() {
        // Arrange
        var deferred = $q.defer();
    
        deferred.resolve('ok');
        modalMock.show.and.returnValue(deferred.promise);
    
        // Act
        $scope.click();
        $scope.$digest(); // Makes Angular resolve the promise
    
        // Assert
        expect(serviceMock.resetPassword).toHaveBeenCalled();
      });
    
      it('should not reset the password when the user cancels', function() {
        // Arrange
        var deferred = $q.defer();
    
        deferred.resolve('cancel');
        modalMock.show.and.returnValue(deferred.promise);
    
        // Act
        $scope.click();
        $scope.$digest(); // Makes Angular resolve the promise
    
        // Assert
        expect(serviceMock.resetPassword).not.toHaveBeenCalled();
      });
    });
    

    每个测试中的模拟安排代码可以移动到每个
    之前的
    部分,这样它就不会重复。我这样做不是为了让事情变得简单