我们正在构建一个AngularJS应用程序并利用karma / jasmine进行单元测试。 在我们运行业力并且能够成功运行测试之后,我们遇到了一个非常奇怪的错误。
出于某种原因,我们不能运行超过14个单独的测试。 不知何故,这个数字似乎是相关的,因为我删除或添加哪些测试并不重要。
目前有两个控制器和一个服务,都有测试。
当我们为任何规范添加任何更多测试时,gulp test-unit
将失败并显示注入器错误,它指出,它无法加载模块,没有太多细节。
即使新测试只是expect(true).toEqual(true);
正如我所写,在我们添加的测试位置和测试中并不重要,它总是失败超过14次测试。
即使我们删除了所有测试,也只是删除了" true = true"测试。 我在karma配置中找不到任何内容,这限制了可以运行的测试数量。我也没有在网上找到类似的东西。 我什么看不到?
(我知道命名不一致)
我现在已经观察到,根据业力调试日志,这些测试运行成功。尽管如此,错误仍然存在。但毕竟这可能不是业力问题。
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);
};
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
]
};
};
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();
});
/**/
});
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();
});
});
});
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)
对于可能出现的问题的任何见解将不胜感激。
答案 0 :(得分:0)
事实证明,angular-cookies并没有打包到vendor.js。
在我们开始使用未压缩的代码进行测试后,结果显示出来,我们能够获得一些可读的错误消息。从那以后很短的时间内很明显。该模块没有加载,因为它不可用。
所以我们添加了
{"chunks": {
...
"vendor":{
....
"angular-cookies",
...
}
}
}
和
{"chunks": {
...
"paths_uncompressed":{
....
"angular-cookies": "bower_components/angular-cookies/angular-cookies.js"
...
}
}
}
就是这样。谢谢!