14次测试后的Angular / Karma单元测试错误 - 无论测试本身

时间:2016-07-22 17:34:15

标签: angularjs unit-testing jasmine karma-runner

一般问题

我们正在构建一个AngularJS应用程序并利用karma / jasmine进行单元测试。 在我们运行业力并且能够成功运行测试之后,我们遇到了一个非常奇怪的错误。

出于某种原因,我们不能运行超过14个单独的测试。 不知何故,这个数字似乎是相关的,因为我删除或添加哪些测试并不重要。

目前有两个控制器和一个服务,都有测试。

当我们为任何规范添加任何更多测试时,gulp test-unit将失败并显示注入器错误,它指出,它无法加载模块,没有太多细节。 即使新测试只是expect(true).toEqual(true); 正如我所写,在我们添加的测试位置和测试中并不重要,它总是失败超过14次测试。

即使我们删除了所有测试,也只是删除了" true = true"测试。 我在karma配置中找不到任何内容,这限制了可以运行的测试数量。我也没有在网上找到类似的东西。 我什么看不到?

(我知道命名不一致)

更新

我现在已经观察到,根据业力调试日志,这些测试运行成功。尽管如此,错误仍然存​​在。但毕竟这可能不是业力问题。

这是我们的Karma配置:

果报unit.conf.js

var sharedConfig = require('./karma-shared.conf');

module.exports = function (config) {
  var conf = sharedConfig();

  conf.files = conf.files.concat([

    //extra testing code
    'bower_components/angular-mocks/angular-mocks.js',

    //mocha stuff
    'tests/mocha.conf.js',

    //test files
    './tests/unit/**/*.js'
  ]);

  config.set(conf);
};

果报shared.conf.js

module.exports = function() {
    return {
        basePath: '../',
        frameworks: ['jasmine','mocha'],
        reporters: ['progress'],
        browsers: ['Chrome', 'Safari'],
        autoWatch: true,

        // these are default values anyway
        singleRun: false,
        colors: true,

        files : [
            //3rd Party Code

            //App-specific Code
            'build/vendor.js',
            'build/vendor.ui.js',
            'build/vendor.datatables.js',
            'build/app.js',

            //Test-Specific Code

        ]
    };
};

这些是三个规格:

LoginCtrlSpec.js

describe('LoginController', function () {
  var loginCtrl`enter code here`
    , scope
    , UserService
    , $controller
    , $rootScope
    , validUser = 'valid@email.tld'
    , inValidUser = 'someinvaliduser@nowhereto.go'
    , password = 'superst(r/l)ongpassword'
    , backendIsFunctional = true
    , theCookies = {}
    ;
  var userObject = {
    email: validUser,
    first_name: 'Maria',
    last_name: 'Tortilla',
    id: 1,
    authentication_token: 'thisisaveryvalidtoken'
  };
// load other modules and provide services
  beforeEach(function () {
    module('app.auth');
    module('ui.router');
    module(function ($provide) {
      /**/
      $provide.service('UserService', function () {
        this.login = function (email, password) {
          //prepare responses for success and failure
          var successResponse = {
            first_name: 'Maria',
            last_name: 'Tortilla',
            authentication_token: '69XyesVbU4ja8HYGtTN4',
            authentication_token_created_at: '2016-07-20T11:34:42.567Z',
            has_changed_password: true,
            data: {
              user: {}
            }
          };
          var failureResponse = {
            status: 401,
            data: {
              user: {}
            }
          };
          //check credentials
          if (
            email === 'valid@email.tld' &&
            password === 'superst(r/l)ongpassword'
          ) {
            //return success response
            return {
              then: function (success, error) {
                success(successResponse);
              }
            }
          } else {
            //return error response
            return {
              then: function (success, error) {
                error(failureResponse);
              }
            }
          }
        };
        this.getCurrentUser = function () {
          return {
            then: function (success, error) {
              if (backendIsFunctional) {
                success(userObject);
              } else {
                error({});
              }
            }
          }
        };
        this.updateCurrentUser = function () {
          return {
            then: function (callback) {
              callback(userObject);
            }
          }
        };
        this.setCurrentUser = function (user) {
          this.currentUser = user;
        };
        this.currentUser = {};
      });
      /**/
      $provide.service('lazyScript', function () {
        return {
          register: function (args) {
            return {
              then: function (callback) {
              }
            }
          }
        }
      });
      /**/
      $provide.service('$state', function () {
        this.go = jasmine.createSpy();
      });
      /**/
      $provide.service('$cookies', function () {
        return {
          putObject: function (name, data) {
            // console.log('put '+name+' with '+data);
            theCookies[name] = data;
          },
          remove: function (name) {
            // console.log('removed cookie '+name);
            delete theCookies[name];
          },
          getObject: function (name) {
            return theCookies[name];
          }
        };
      });
      /**/
    });
  });
  // inject dependencies
  beforeEach(inject(function ($injector) {
    $controller = $injector.get('$controller');
    $rootScope = $injector.get('$rootScope');
    // $state = $injector.get('$state');
    scope = $rootScope.$new();
    loginCtrl = $controller('LoginCtrl', {
      '$scope': scope
    });
  }));
  // perform some tests
  /**/
  it('forwards the user to the dashboard after successful login', function () {
    expect(loginCtrl).not.toBe(null);
    spyOn(scope, 'finishAndEnter');
    scope.email = validUser;
    scope.password = password;
    scope.login();
    $rootScope.$apply();
    expect(scope.finishAndEnter).toHaveBeenCalled();
  });
  /**/
  it('denies access with invalid credentials', function () {
    spyOn(scope, 'finishAndEnter');
    scope.email = inValidUser;
    scope.login();
    $rootScope.$apply();
    expect(scope.finishAndEnter).not.toHaveBeenCalled();
  });
  /**/
  it('should not let the user complete her profile without first or last name', function () {
    spyOn(scope, 'demandPersonalInfo');
    scope.updateDetails = {first_name: undefined, last_name: undefined};
    scope.updateUserDetails();
    expect(scope.demandPersonalInfo).toHaveBeenCalled();
  });
  /**/
  it('should not let the user complete her profile without only first name', function () {
    spyOn(scope, 'demandPersonalInfo');
    scope.updateDetails = {first_name: 'Maria', last_name: undefined};
    scope.updateUserDetails();
    expect(scope.demandPersonalInfo).toHaveBeenCalled();
  });
  /**/
  it('should not let the user complete her profile without only last name', function () {
    spyOn(scope, 'demandPersonalInfo');
    scope.updateDetails = {first_name: undefined, last_name: 'Tortilla'};
    scope.updateUserDetails();
    expect(scope.demandPersonalInfo).toHaveBeenCalled();
  });
  /**/
   it('should let a user update her profile with first and last name', function () {
    spyOn(scope, 'demandPersonalInfo');
    scope.updateDetails = {first_name: 'Maria', last_name: 'Tortilla'};
    console.log(scope.updateDetails);
    scope.updateUserDetails();
    expect(scope.demandPersonalInfo).not.toHaveBeenCalled();
  });
   /**/
});

AdminControllerSpec.js

describe('Unit: testing AdministrationController Controller module', function () {
  'use strict';
  //references to mocked services
  var mockUserService;


  beforeEach(function () {
    //mock userservice
    module(function ($provide) {
      $provide.service('UserService', function () {
        this.getCurrentUser = jasmine.createSpy('getCurrentUser');
      });
    });

    //load admin module
    module('app.admin');
  });

  var adminCtrl, scope;

  beforeEach(inject(function ($controller, $rootScope, UserService) {

    //catch injected UserService and assign to mocked one
    mockUserService = UserService;

    scope = $rootScope.$new();
    adminCtrl = $controller('AdministrationController', {
      '$scope': scope
    });
  }));

  describe('AdministrationController', function () {

    it('AdministrationController should be existing', function () {
      expect(adminCtrl).not.toBe(null);
    });
    it('means that hello is hello', function () {
      expect(scope.hello).toEqual('Hello');
    });
    it('is helgig', function () {
      //call a method that uses the userservice
      scope.login();

      //the userservice should have been called from login()
      expect(mockUserService.getCurrentUser).toHaveBeenCalled();

    });
  });

});

UserServiceSpec.js

describe('UserService', function () {

  var UserService;

  var validUserName = 'validuser@domain.tld';
  var validUserPassword = 'goopassword';
  var inValidUserName = 'invaliduser@domain.tld';
  var backendIsWorking = true;
  // var qReturnesUser = false;
  var validUser = {
    'email': 'validuser@domain.tld',
    'first_name': 'Maria',
    'last_name': 'Tortilla',
    'authentication_token': 'yeahSomeT0k3nIThink_dontYu?'
  };

  var updatedUser = {
    'first_name': 'Harro',
    'email': 'validuser@domain.tld',
    'last_name': 'Tortilla',
    'authentication_token': 'yeahSomeT0k3nIThink_dontYu?'
  };

  var updateUserResponse = {
    status: 200,
    user: updatedUser
  };

  var httpSuccessResponse = {
    data: validUser
  };
  var httpErrorResponse = {
    data: {
      status: 401,
      user: {}
    }
  };

  //mocked cookie dictionary
  var theCookies = {};

  //load modules
  beforeEach(function () {
    module('ngResource');
    module('app.common');
    module(function ($provide) {

      $provide.service('$q', function () {
        return {
          defer: function () {
            return {
              resolve: function () {
              },
              reject: function () {
              },
              promise:{
                then: function(){}
              }
            };
          }
        };
      });
      $provide.service('$http', function () {
        return function (config) {

          var pw = config.data.password;
          var user = config.data.email;

          var responseObject = {};

          //login and logout response
          if (config.url.indexOf('sign_in') >= 0 || config.url.indexOf('sign_out') >= 0) {
            responseObject = {
              //success portion of response
              success: function (callback) {
                var response;

                //decide which actual response should be returned
                if (pw === validUserPassword && user === validUserName) {
                  response = httpSuccessResponse;
                  callback(response);
                } else {
                  response = httpErrorResponse;
                }
                //return chained error portion
                return {
                  error: function (callback) {
                    callback(response);
                  }
                }
              }
            };
          }

          return responseObject;
        }
      });
      $provide.service('$cookies', function () {
        return {
          putObject: function (name, data) {
            // console.log('put '+name+' with '+data);
            theCookies[name] = data;
          },
          remove: function (name) {
            // console.log('removed cookie '+name);
            delete theCookies[name];
          },
          getObject: function(name){
            return theCookies[name];
          }
        };
      });
      $provide.factory('User', function () {
        return {
          update: function (user, successCallback, errorCallback) {

            if (backendIsWorking) {
              successCallback(updateUserResponse);
            } else {
              errorCallback(httpErrorResponse);
            }
          },
          get: function (user, successCallback, errorCallback) {
            if (backendIsWorking) {
              successCallback(validUser);
            } else {
              errorCallback(httpErrorResponse);
            }
          }
        }
      });
    });
  });

  //inject stuff
  beforeEach(inject(function ($injector) {
    // UserService = _UserService_;
    $resource = $injector.get('$resource');
    UserService = $injector.get('UserService');
  }));

  //do the tests
  it('should have UserSerivce defined', function () {
    // console.log(UserService);
    expect(UserService).toBeDefined();
  });

  it('should have a user object after login', function () {
    UserService.login(validUserName, validUserPassword);
    expect(UserService.currentUser).toBeDefined();
    expect(UserService.currentUser).toEqual(validUser);
  });

  it('should have a empty user object after invalid login', function () {
    UserService.login(inValidUserName, validUserPassword);
    expect(UserService.currentUser).toBeDefined();
    expect(UserService.currentUser).toEqual(null);
  });

  it('should clear the user object after logout', function () {
    UserService.currentUser = validUser;
    expect(UserService.currentUser).toBeDefined();
    UserService.logout();
    expect(UserService.currentUser).toBe(null);
  });

  it('should set the current user', function () {
    expect(UserService.currentUser).toBe(null);
    UserService.setCurrentUser(validUser);
    expect(UserService.currentUser).toEqual(validUser);
  });

  it('should update the users data', function () {
    spyOn(UserService, 'setCurrentUser');
    UserService.currentUser = validUser;
    UserService.updateCurrentUser();
    expect(UserService.setCurrentUser).toHaveBeenCalledWith(updatedUser);
  });
});

最后,这是我们得到的错误:

无论我们删除哪个测试,它总是完全相同的错误。

 Error: [$injector:modulerr] http://errors.angularjs.org/1.5.8/$injector/modulerr?p0=app&p1=%5B%24injector%3Amodulerr%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.5.8%2F%24injector%2Fmodulerr%3Fp0%3DngCookies%26p1%3D%255B%2524injector%253Anomod%255D%2520http%253A%252F%252Ferrors.angularjs.org%252F1.5.8%252F%2524injector%252Fnomod%253Fp0%253DngCookies%250Ahttp%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A129%253A417%250Ahttp%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A148%253A100%250Ab%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A147%253A143%250Ahttp%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A147%253A386%250Ahttp%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A162%253A473%250Aq%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A130%253A359%250Ag%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A162%253A320%250Ahttp%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A162%253A489%250Aq%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A130%253A359%250Ag%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A162%253A320%250Acb%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A166%253A337%250Ac%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A143%253A392%250ABc%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A144%253A180%250Ahttp%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fapp.js%253F3b907e783e3f83ec76cf288ed98d542361f9ed3a%253A287%253A22%250Aj%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A2%253A26930%250AfireWith%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A2%253A27739%250Aready%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A2%253A29543%250AI%2540http%253A%252F%252Flocalhost%253A9876%252Fbase%252Fbuild%252Fvendor.js%253F9b2b4c4d081dd795c95280a4f7af90157c7ae917%253A2%253A29728%0Ahttp%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A129%3A417%0Ahttp%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A163%3A224%0Aq%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A130%3A359%0Ag%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A162%3A320%0Ahttp%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A162%3A489%0Aq%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A130%3A359%0Ag%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A162%3A320%0Acb%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A166%3A337%0Ac%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A143%3A392%0ABc%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A144%3A180%0Ahttp%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fapp.js%3F3b907e783e3f83ec76cf288ed98d542361f9ed3a%3A287%3A22%0Aj%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A2%3A26930%0AfireWith%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A2%3A27739%0Aready%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A2%3A29543%0AI%40http%3A%2F%2Flocalhost%3A9876%2Fbase%2Fbuild%2Fvendor.js%3F9b2b4c4d081dd795c95280a4f7af90157c7ae917%3A2%3A29728
at build/vendor.js:163
Chrome 52.0.2743 (Mac OS X 10.11.5): Executed 15 of 15 ERROR (0.187 secs / 0.123 secs)
Safari 9.1.1 (Mac OS X 10.11.5): Executed 15 of 15 ERROR (0.005 secs / 0.045 secs)

对于可能出现的问题的任何见解将不胜感激。

1 个答案:

答案 0 :(得分:0)

事实证明,angular-cookies并没有打包到vendor.js。

在我们开始使用未压缩的代码进行测试后,结果显示出来,我们能够获得一些可读的错误消息。从那以后很短的时间内很明显。该模块没有加载,因为它不可用。

所以我们添加了

{"chunks": {
    ...
    "vendor":{
        ....
        "angular-cookies",
        ...
    }
  }
} 

 {"chunks": {
    ...
    "paths_uncompressed":{
        ....
         "angular-cookies": "bower_components/angular-cookies/angular-cookies.js"
        ...
    }
  }
} 

就是这样。谢谢!