如何使用Jasmine测试AngularJS中的.catch Promise返回?

时间:2014-05-03 02:09:31

标签: javascript angularjs unit-testing jasmine promise

我对Javascript很新,只是学习AngularJS,但我已经将大部分测试用例与我发现的一些很好的例子一起使用。不幸的是,我似乎无法找到任何可以帮助我测试当前案例的内容。

我使用模拟服务测试Controller,其方法返回一个promise。我希望模拟的服务返回错误,以便执行' .catch'阻塞在控制器方法中。我可以说它没有通过几种方式正确调用:

  1. 我使用istanbul进行代码覆盖,而且它告诉我我没有覆盖' catch'
  2. ' .catch'中的代码阻止没有通过调试
  3. 执行

    被测控制器,具体需要测试' .catch'在$ scope.login中:

    login.js

    'use strict';
    
    angular.module('ibcwebDashApp')
      .controller('LoginCtrl', function ($scope, Auth, $location) {
        $scope.user = {};
        $scope.errors = {};
    
        $scope.login = function(form) {
          $scope.submitted = true;
    
          if(form.$valid) {
            Auth.login({
              email: $scope.user.email,
              password: $scope.user.password
            })
            .then( function() {
              // Logged in, redirect to home
              $location.path('/');
            })
            .catch( function(err) {
              err = err.data;
              $scope.errors.other = err.message;
            });
          }
        };
      });
    

    我试图模仿的服务和方法:

    Auth.login

    'use strict';
    
    angular.module('ibcwebDashApp')
      .factory('Auth', function Auth($location, $rootScope, Session, User, $cookieStore) {
    
        // Get currentUser from cookie
        $rootScope.currentUser = $cookieStore.get('user') || null;
        $cookieStore.remove('user');
    
        return {
    
          /**
           * Authenticate user
           * 
           * @param  {Object}   user     - login info
           * @param  {Function} callback - optional
           * @return {Promise}            
           */
          login: function(user, callback) {
            var cb = callback || angular.noop;
    
            return Session.save({
              email: user.email,
              password: user.password
            }, function(user) {
              $rootScope.currentUser = user;
              return cb();
            }, function(err) {
              return cb(err);
            }).$promise;
          },
    

    最后,我的测试文件。有趣的是,所有测试都在通过,但是期望'在最后一次测试中可以改为几乎任何东西而且它仍然通过。前两个测试似乎按预期运行,但最后一个测试是我试图通过从模拟Auth服务抛出错误来执行catch块:

    login.unit.js

    'use strict';
    
    describe('Controller: LoginCtrl', function () {
      var $scope, $location, loginCtrl, mockAuthService;
    
    
      beforeEach(function() {
        mockAuthService = jasmine.createSpyObj('Auth', ['login']);
    
        module('ibcwebDashApp');
    
        module(function($provide) {
          $provide.value('Auth', mockAuthService);
        });
    
        inject(function($rootScope, $controller, $q, _$location_) {
          //create an empty scope
          $scope = $rootScope.$new();
          $location = _$location_;
          //declare the controller and inject our empty scope
          loginCtrl = $controller('LoginCtrl', {$scope: $scope, Auth: mockAuthService});
    
        });
    
      });
    
    
      describe('successful login', function() {
    
        beforeEach(function() {
          inject(function($q) {
            mockAuthService.login.andReturn($q.when());
          });
    
        });
    
        it('should call auth.login with the scope email and password when form is valid', function() {
          //given
          $scope.form = {};
          $scope.form.$valid = true;
          $scope.user.email = 'user@test.com';
          $scope.user.password = 'password123';
    
          //when
          $scope.login($scope.form);
    
          //then
          expect($scope.submitted).toBe(true);
          expect(mockAuthService.login).toHaveBeenCalledWith({email:'user@test.com', password:'password123'});
    
          $scope.$apply(); //force return of auth.login promise
    
          expect($location.path()).toBe('/');
        });
    
        it('should not call auth.login if form is invalid', function() {
          //given
          $scope.form = {};
          $scope.form.$valid = false;
    
          //when
          $scope.login($scope.form);
    
          expect(mockAuthService.login).not.toHaveBeenCalled();
        });
      });
    
      describe('unsuccessful login', function() {
    
        beforeEach(function () {
          inject(function () {
            mockAuthService.login.andReturn($q.when(new Error('Bad Login!')));
          });
    
          it('should set errors.other to the returned auth error message', function() {
            //given
            $scope.form = {};
            $scope.form.$valid = true;
    
            //when
            $scope.login($scope.form);
    
            $scope.$apply();
    
            //then
            expect($scope.errors.other).toEqual('Bad Login!');
          });
    
        });
      });
    });
    

    我为发布这么多代码而道歉,但我想提供尽可能多的上下文。我非常感谢任何可以帮助我的人,因为我学习了单元测试Angular和承诺的方法!感谢!!!

    **更新**

    我能够通过下面的帮助解决我的问题并发现一些语法错误。以下是解决这个问题的原因:

    1. 上次测试时我的beforeEach没有正确关闭,实际上包含了最后一次测试,导致它无法正常运行(或者可能根本没有运行)。这就是改变期望条件导致没有错误的原因。
    2. 我使用下面建议的beforeEachmockAuthService.login.andReturn($q.reject({data: {message: 'Bad Login!'}}));注入更改为:reject
    3. 一旦我正确关闭beforeEach,我收到一条错误消息,指出$ q未定义,因此我必须将其添加到inject(function($q)
    4. 我纠正了这些问题后,承诺被正确拒绝,错误被控制器中的相应代码捕获。

1 个答案:

答案 0 :(得分:9)

在运行测试之前或期间,模拟出部分环境:

var originalAuthLogin = Auth.login;
Auth.login = function() {
  return Promise.reject({data: {message: 'Error message'}});
};

测试结束后恢复环境:

Auth.login = originalAuthLogin;

这会立即调用您尝试测试的代码的.catch()块。