将服务注入AngularJS上的Jasmine单元测试
这让我发疯了,我无法进行一个相当简单的单元测试(这是我第一次使用AngularJS) 我会让一切尽可能简单。我一直在尝试几种注入服务的方法,但都不起作用。我最后一次尝试的是来自 这是我的测试文件:将服务注入AngularJS上的Jasmine单元测试,angularjs,unit-testing,jasmine,Angularjs,Unit Testing,Jasmine,这让我发疯了,我无法进行一个相当简单的单元测试(这是我第一次使用AngularJS) 我会让一切尽可能简单。我一直在尝试几种注入服务的方法,但都不起作用。我最后一次尝试的是来自 这是我的测试文件: describe('product service',function(){ var ProductService; beforeEach(module('inspinia')); beforeEach(inject(function (_ProductService_) {
describe('product service',function(){
var ProductService;
beforeEach(module('inspinia'));
beforeEach(inject(function (_ProductService_) {
ProductService = _ProductService_;
}));
describe('Test',function(){
it("should be aware that true is true", function() {
expect(true).toBe(true);
});
});
});
这是我的服务:
/**
* Product service.
*
* Service that manages products. Including offline mode and sync tasks.
*
* @param {!angular.Http} $http
* @param {!angular.RootScope} $rootScope
* @ngInject
* @export
*/
(function () {
'use strict';
angular
.module('inspinia')
.factory('ProductService', ProductService);
/**
* clientUpdatesQueue
*
* Will hold all the offline operations on the following format:
* {'Section':SectionEnum.SECTION, 'Operation':OperationEnum.OPERATION, 'Record':record, 'Timestamp':currentTimeStamp}
*/
var clientUpdatesQueue = [];
var products = [];
/**
* Enum for sections used by the clientUpdatesQueue array.
*/
var SectionEnum = {
PRODUCTS : 0
};
/**
* Enum for operations used by the clientUpdatesQueue array.
*/
var OperationEnum = {
CREATE : 0,
UPDATE : 1,
DELETE : 2
};
/**
* Initializes the client updates queue
*/
(function initClientUpdatesQueue(){
clientUpdatesQueue = angular.fromJson(localStorage.clientUpdatesQueue || "[]");
if(localStorage.products === undefined){
//GetAllFromServer();
}
clientUpdatesQueue = angular.fromJson(localStorage.clientUpdatesQueue || "[]");
})();
ProductService.$inject = ['$http', '$rootScope'];
function ProductService($http, $rootScope) {
/**
* TODO
* Will write a function that sends the offline operations updates to the server
*
* service.SendUpdates = SendUpdates;
*/
var service = {};
service.GetAllFromServer = GetAllFromServer;
service.GetAll = GetAll;
service.Get = Get;
service.Create = Create;
service.Update = Update;
service.Delete = Delete;
service.Synchronize = Synchronize;
return service;
/***************SYNCHRONIZATION TASKS***************
***************************************************/
/**
* Synchronize
* Iterates through the pending updates queue and performs the corresponding call to the server
*/
function Synchronize(){
for (var key in clientUpdatesQueue) {
switch(clientUpdatesQueue[key].Operation){
case 0:
CreateOnServer(clientUpdatesQueue[key].Record);
break;
case 1:
UpdateOnServer(clientUpdatesQueue[key].Record);
break;
case 2:
DeleteOnServer(clientUpdatesQueue[key].Record);
break;
}
clientUpdatesQueue.splice(key, 1);
}
updateLocalStorage();
}
/**
* updateLocalStorage
* Updates local storage with the lastest operations.
*/
function updateLocalStorage(){
localStorage.products = angular.toJson(products || "[]");
localStorage.clientUpdatesQueue = angular.toJson(clientUpdatesQueue || "[]");
}
/**
* GetAllFromServer
* Gets all products matching the current session from the server and store them on the local storage.
*/
function GetAllFromServer(){
var session = angular.fromJson(localStorage.session);
$http({
method: 'GET',
url: $rootScope.apiURL+'getAllClientProducts/'+session,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function(response){
if(response.ErrorMessage === null && response.Result !== null){
localStorage.products = angular.toJson(Object.keys(response.Result).map(function (key) {return response.Result[key]}));
}else if(response.ErrorMessage !==null){
alert(response.ErrorMesage);
}
})
.error(function(response){
alert('Something went wrong. Please try again: '+response);
});
}
/***************LOCAL TASKS*************************
***************************************************/
/**
* GetAll
* Gets all the products from the local storage
*
* @return {Array} products
*/
function GetAll(){
if(localStorage.products !== undefined) {
return angular.fromJson(localStorage.products);
}else{
GetAllFromServer();
}
}
/**
* Gets the specified product by its primary key
*
* @param {String} InvProductId
* @return {Object} product
*/
function Get(id){
products = GetAll();
var thisProduct = products.filter(function(p){
return p.InvProductId === id;
});
updateLocalStorage();
return thisProduct[0];
}
/**
* Creates a product
*
* @param {Object} product
*/
function Create(product){
var result = true;
if(!ValidateSnapshot(product)){
return false;
}
products = GetAll();
products.push(product);
clientUpdatesQueue.push({'Section':SectionEnum.PRODUCTS, 'Operation':OperationEnum.CREATE, 'Record':product, 'Timestamp':Date.now()});
updateLocalStorage();
return result;
}
/**
* Updates a product
*
* @param {Object} product
*/
function Update(product){
var result = true;
if(!ValidateSnapshot(product)){
return false;
}
products = GetAll();
for (var key in products) {
if(products[key].InvProductId === product.InvProductId){
products[key] = product;
}
}
clientUpdatesQueue.push({'Section':SectionEnum.PRODUCTS, 'Operation':OperationEnum.UPDATE, 'Record':product, 'Timestamp':Date.now()});
updateLocalStorage();
return result;
}
/**
* Deletes a product
*
* @param {Object} product
*/
function Delete(product){
var result = true;
products = GetAll();
for (var key in products) {
if(products[key].InvProductId === product.InvProductId){
products.splice(key, 1);
}
}
clientUpdatesQueue.push({'Section':SectionEnum.PRODUCTS, 'Operation':OperationEnum.DELETE, 'Record':product, 'Timestamp':Date.now()});
updateLocalStorage();
return result;
}
/***************SERVER COMMUNICATION****************
***************************************************/
/**
* Creates a product on the server
*
* @param {Object} product
*/
function CreateOnServer(product){
var session = angular.fromJson(localStorage.session);
$http({
method: 'POST',
url: $rootScope.apiURL+'createProduct/'+session,
data: $.param(product),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function(response){
if(response.ErrorMessage === null && response.Result !== null){
mixpanel.track("Product successfuly created at server: " + response.Result.InvProductId);
}
})
.error(function(data){
mixpanel.track("Create Product Went Wrong: "+data);
alert('Something went wrong with product creation: '+data);
});
}
/**
* Updates a product on the server
*
* @param {Object} product
*/
function UpdateOnServer(product){
var session = angular.fromJson(localStorage.session);
$http({
method: 'POST',
url: $rootScope.apiURL+'updateProduct/'+session,
data: $.param(product),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function(response){
if(response.ErrorMessage === null && response.Result !== null){
mixpanel.track("Product successfuly edited: " + response.Result.InvProductId);
}
})
.error(function(data){
mixpanel.track("Create Product Went Wrong: "+data);
alert('Something went wrong with product creation: '+data);
});
}
/**
* TODO
* Deletes a product on the server
*
* @param {Object} product
*/
function DeleteOnServer(product){
return true;
}
/***************VALIDATION UTILITIES****************
***************************************************/
function ValidateSnapshot(product){
var result = true;
if(product === null || product === undefined){
return false;
}
if(!product.ApplicableTaxKeys.split(',') instanceof Array || product.ApplicableTaxKeys !== null){
return false;
}
if(product.Barcode.length < 1 || product.Barcode === null || product.Barcode === undefined){
return false;
}
if(product.CliClientId.length !== 10){
return false;
}
if(product.Description.length < 1 || product.Description === null || product.Description === undefined){
return false;
}
if(product.InternalCode.length < 1 || product.InternalCode === null || product.InternalCode === undefined){
return false;
}
if(!product.InvProductCategoryId.split(',') instanceof Array || product.InvProductCategoryId !== null){
return false;
}
if(product.Name.length < 1 || product.Name === null || product.Name === undefined){
return false;
}
if(product.SaleUnitType.length < 1 || product.SaleUnitType === null || product.SaleUnitType === undefined){
return false;
}
if(product.Status.length < 1 || product.Status === null || product.Status === undefined){
return false;
}
if(product.UnitMeasure.length < 1 || product.UnitMeasure === null || product.UnitMeasure === undefined){
return false;
}
return result;
}
}
})();
如果我删除尝试注入服务的行,测试就会通过,所以我认为其他一切都很好
Karma配置文件:
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'angular/angular.min.js',
'../node_modules/angular-mocks/angular-mocks.js',
'app.js',
'app-services/*.js',
'app-services-tests/*.js'
],
exclude: [
],
preprocessors: {
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'],
singleRun: false,
concurrency: Infinity
})
}
编辑:我将我的karma conf文件向上移动到一个目录,以丢弃与../符号有关的任何奇怪行为。但我还是犯了同样的错误。我最终得到了这个conf文件:
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'js/angular/angular.min.js',
'node_modules/angular-mocks/angular-mocks.js',
'js/app.js',
'js/app-services/*.js',
'js/app-services-tests/*.js'
],
exclude: [
],
preprocessors: {
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'],
singleRun: false,
concurrency: Infinity
})
}
你这么说
删除我尝试注入服务的行后,测试通过
因为您的测试用例没有使用任何角度模拟的东西
他们纯粹使用了茉莉花的东西
这意味着您的angular-mocks.js没有加载。
我用相同的代码进行了检查,唯一的区别是加载angularmocks.js
库
请检查karma\u配置文件中angular mocks.js
的路径
在Karma config
中查找基本路径后,angular mocks.js
的路径应该是这样的angular/angular.min.js
如果您的node\u modules
位于angular
文件夹之外,请使用此路径'../node\u modules/angular mocks/angular mocks.js'
编辑:请更新此
files: [
'js/angular/angular.min.js',
'node_modules/angular-mocks/angular-mocks.js',
'js/app-services/*.js',
'js/app.js',
'js/app-services-tests/*.js'
],
更新:
好的,解决方案是,您应该定义一个主要模块,即您的ng应用程序
然后为服务、控制器等创建模块,并将它们注入到一个模块中
考虑到这些因素,为ProductService
和相关服务定义一个模块,例如serviceModule
angular.module('serviceModule',[])
//service.js
将其注入主模块
var-app=angular.module('inspinia',['serviceModule'])
/app.js你能分享karma配置文件吗谢谢,我已经更新了我的问题(我已经有了你指给我的最后一条路径)。你的js文件夹和karma js在你的应用程序目录中处于同一级别是的。我的karma配置文件、js文件夹和noode_模块文件夹处于同一级别。谢谢。按照该顺序(app.js之前的app services),它会给我以下错误:“模块'inspinia'不可用!您可能拼错了模块名称或忘记加载它。如果注册模块,请确保将依赖项指定为第二个参数。”。交换订单(app.js在app services之前)会产生与之前相同的错误。不,那么service.js模块声明有问题
files: [
'js/angular/angular.min.js',
'node_modules/angular-mocks/angular-mocks.js',
'js/app-services/*.js',
'js/app.js',
'js/app-services-tests/*.js'
],