将 mockService 注入到从指令初始化的控制器中

Inject mockService into an controller initialized from a directive

我想对一个指令进行单元测试 (karma),它有一个带有注入服务的控制器。

见以下代码:

angular
    .module('webkr')
    .directive('userChangePassword', userChangePassword)
    .directive('compareTo', compareTo);


  /** @ngInject */
  function userChangePassword() {
    var directive = {
      restrict: 'E',
      scope: {},
      templateUrl: 'app/components/account/user_change_password/user_change_password.html',
      controller: changePasswordController,
      controllerAs: 'vm',
      bindToController: true
    };

    return directive;

    /** @ngInject */
    function changePasswordController($log, User, toastr) {
      var _this = this;

      //properties
      _this.formModel = {
        password: null,
        oldPassword: null,
        passwordRepetition: null
      };
      _this.formDefinition = {
        oldPassword: {
          name: 'oldPassword',
          label: 'Altes Passwort',
          placeholder: 'Altes Passwort',
          error: 'old password is required.'
        },
        password: {
          name: 'password',
          label: 'Neues Passwort',
          placeholder: 'Neues Passwort',
          error: 'new password is required.'
        },
        passwordRepetition: {
          name: 'passwordRepetition',
          label: 'Passwort bestätigen',
          placeholder: 'Passwort bestätigen',
          errorRequired: 'old password is required.',
          errorInvalid: 'Confirmation password is not equal new password.'
        }
      };

      //methods

      /**
       * cancel change password procedure
       */
      _this.cancelChangePassword = function () {
        //clean form data

        _this.changePasswordForm.$setPristine();
      };
      /**
       * submit change password
       */
      _this.submitPasswordChange = function () {
        if (_this.changePasswordForm.$invalid) {
          return;
        }

        User.changePassword(_this.formModel).then(function () {
          toastr.info('Password changed', JSON.stringify(_this.formModel));
          _this.cancelChangePassword();
        }, function (err) {
          toastr.error('Can`t change password');
          $log.error(err);
        });
      };
    }
  }

相应地我的单元测试:

(function () {
  'use strict';

  describe('directive user_change_password', function () {
    var el, compile, rootScope, controller, mockUserService;

    beforeEach(function () {
      mockUserService = jasmine.createSpyObj('User', ['changePassword'])
      module('webkr', 'ngMockE2E')
    });

    beforeEach(inject(function ($compile, $rootScope, $controller, $q, $log, toastr) {
      compile = $compile;
      rootScope = $rootScope;

      //Service routes used in controller
      mockUserService.changePassword.and.returnValue($q.when("result"));

      //Compile element
      el = compile("<user-change-password></user-change-password>")(rootScope);
      rootScope.$digest();

      //Init controller
      controller = el.controller("userChangePassword", {
        $log: $log,
        User: mockUserService,
        toastr: toastr
      });

      //Spy on the form
      spyOn(controller.changePasswordForm, '$setPristine')

    }));

    it('should be compiled', function () {
      expect(el.html()).not.toEqual(null);
    });
  });
})();

不知何故,控制器没有正确初始化。当我删除对象 ({$log:$log,User:mockUserService,toastr:toastr}) 时,一切正常。我在这里做错了什么?

模拟控制器依赖项适用于涉及 $controller.

的仅控制器规范

el.controller 是 getter 方法并且 has only 1 parameter:

controller(name) - retrieves the controller of the current element or its parent.

此刻

  //Init controller
  controller = el.controller("userChangePassword", { ... });

指令已编译且其控制器已实例化,这就是可以检索其实例的原因。

在测试指令控制器时模拟服务坚持在注入器级别模拟:

beforeEach(module('webkr', 'ngMockE2E', function ($provide) {
  $provide.factory('User', function ($q) {
     return { changePassword: jasmine.createSpy().and.returnValue($q.when("result")) };
  });      
}));