尝试在 AngularJS 上对基本服务进行单元测试时出错
Error when trying to unit test a basic service on AngularJS
我是用Jasmine对AngularJS进行单元测试的新手。我正在尝试建立一个非常简单的环境。
这是我的karma.conf.js文件:
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
})
}
在其中一个函数中,我进行了ajax调用,因此据我了解,我可以做这样的事情(这是我的测试文件(:
describe('factory: ProductService',function(){
var ProductService;
beforeEach(inject(function(_ProductService_){
ProductService = _ProductService_;
}));
describe('factory: ProductService',function(){
var ProductService;
beforeEach(inject(function(_ProductService_){
ProductService = _ProductService_;
}));
var categorizedProducts = angular.fromJson('{ "ErrorMessage": null, "Result": { "0": { "CliClientId": "016VK9B90U", "InvProductCategoryId": "0LQIHDH2EM", "CategoryName": "LACA", "InvProductCategoryParentId": null, "ModifiedDate": "2016-01-28 16:36:37", "products": { "0": { "CliClientId": "016VK9B90U", "InvProductId": "030LFCE9KV", "InvProductCategoryId": "0LQIHDH2EM", "Name": "1055-B020 LACA NITRO MATE 4 LTS", "Description": "1055-B020 LACA NITRO MATE 4 LTS-VALRESA", "Barcode": null, "InternalCode": "L105525", "SaleUnitType": "Piece", "ApplicableTaxKeys": null, "Status": "Active", "CreatedDate": "2016-01-28 16:36:39", "CreatedBy": "0000000000", "ModifiedDate": "2016-01-28 16:36:39", "ModifiedBy": "0000000000", "CliLocationId": "2I1G4IHPOB", "prices": null } } }, "1": { "CliClientId": "016VK9B90U", "InvProductCategoryId": "W27Q71D4XJ", "CategoryName": "BROCHAS", "InvProductCategoryParentId": null, "ModifiedDate": "2016-01-28 16:36:38", "products": { "0": { "CliClientId": "016VK9B90U", "InvProductId": "0B23HD5PGL", "InvProductCategoryId": "W27Q71D4XJ", "Name": "BROCHA FAJA ROJA 2 1'/2", "Description": "BROCHA FAJA ROJA 2 1'/2", "Barcode": null, "InternalCode": "BFR2.5", "SaleUnitType": "Piece", "ApplicableTaxKeys": null, "Status": "Active", "CreatedDate": "2016-01-28 16:36:38", "CreatedBy": "0000000000", "ModifiedDate": "2016-01-28 16:36:38", "ModifiedBy": "0000000000", "CliLocationId": "2I1G4IHPOB", "prices": null }, "1": { "CliClientId": "016VK9B90U", "InvProductId": "0FQO5KJMX7", "InvProductCategoryId": "W27Q71D4XJ", "Name": "BROCHA LA BUENA 2'"", "Description": "BROCHA LA BUENA 2'"-BYP", "Barcode": null, "InternalCode": "BBU2", "SaleUnitType": "Piece", "ApplicableTaxKeys": null, "Status": "Active", "CreatedDate": "2016-01-28 16:36:38", "CreatedBy": "0000000000", "ModifiedDate": "2016-01-28 16:36:38", "ModifiedBy": "0000000000", "CliLocationId": "2I1G4IHPOB", "prices": null } } } } }');
it("should return json with categorized products", function () {
httpBackend.whenGET("http://test.dev/v1/getPOSProducts/E59576F6-E25A-4261-B1CA-66A7049C11DD").respond(categorizedProducts);
});
});
});
最后,这是我的产品服务:
/**
* 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;
}
}
})();
当我在终端上运行业力启动时,我收到以下错误:
$ 业力开始 0102 2016 18:41:39.939:警告 [业力]:未捕获 浏览器,打开 http://localhost:9876/01 02 2016 18:41:39.948:INFO [业力]:业力v0.13.19服务器启动于 http://localhost:9876/01 02 2016 18:41:39.953:INFO [启动器]:启动浏览器PhantomJS 01 02 2016 18:41:40.203:INFO [PhantomJS 2.1.1 (Linux 0.0.0(]:已连接 套接字/#yTkMYAHDCJhxIwALAAAA,ID 3944707 PhantomJS 2.1.1 (Linux 0.0.0( 工厂: 产品服务遇到声明异常失败错误: [$injector:unpr] http://errors.angularjs.org/1.3.7/$injector/unpr?p0=ProductServiceProvider%20%3C-%20ProductService(第 38 行( /home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js:38:332 d@/home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js:36:309 /home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js:38:384 d@/home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js:36:309 e@/home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js:37:65 workFn@/home/eduardo/ventamia/vm2/clientside/www-app/node_modules/angular-mocks/angular-mocks.js:2517:26 未定义的语法错误:JSON 解析错误:预期的"}" /home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js (第 14 行( parse@[本机代码] oc@/home/eduardo/ventamia/vm2/clientside/www-app/js/angular/angular.min.js:14:161 /home/eduardo/ventamia/vm2/clientside/www-app/js/app-services-tests/product.service.test.js:7:47 全球 code@/home/eduardo/ventamia/vm2/clientside/www-app/js/app-services-tests/product.service.test.js:1:9 PhantomJS 2.1.1 (Linux 0.0.0(: 执行 1 个(共 1 个(错误 (0.041 个( 秒/0.008 秒( 01 02 2016 18:48:37.489:INFO [观察者]:已更改 文件 "/home/eduardo/ventamia/vm2/clientside/www-app/js/app-services-tests/product.service.test.js"。 PhantomJS 2.1.1 (Linux 0.0.0(: 执行 0 错误中的 0 (0.04 秒/0 secs( ^Ceduardo@vmdev2:~/ventamia/vm2/clientside/www-app/js$ karma 开始 01 02 2016 18:48:42.020:警告 [业力]:没有捕获的浏览器,打开 http://localhost:9876/01 02 2016 18:48:42.029:INFO [业力]:业力 v0.13.19 服务器启动于 http://localhost:9876/01 02 2016 18:48:42.034:INFO [启动器]:启动浏览器PhantomJS 01 02 2016 18:48:42.277:INFO [PhantomJS 2.1.1 (Linux 0.0.0(]:在套接字上连接 /#9-zdfLe9rv-EOnGvAAAA ID 57338630 PhantomJS 2.1.1 (Linux 0.0.0(: 执行 0 个,共 0 个错误(0.037 秒/0 秒(
提前谢谢。
Angular 以以下 URL 的形式为您提供错误消息:
https://docs.angularjs.org/error/$injector/unpr?p0=ProductServiceProvider%20%3C-%20ProductService
它说它不知道如何找到你的ProductService
.
在工厂中,您将ProductService
声明为位于名为 inspinia
的模块中,因此您需要在测试运行之前加载此模块。
在现有beforeEach
之前添加以下内容:
beforeEach(module('inspinia'));
然后 Angular 应该找到服务并能够注入它。
PS:您在测试中有一些重复的代码,您可以删除第二次出现的var ProductService;
和beforeEach
,只使用外部代码。
- Jasmine单元测试在监视服务方法时失败
- Angular 2和服务单元测试
- 在http服务单元测试中使用request-js时,chai.request不是一个函数
- $cacheFactory removeAll() 在服务中的单元测试在 Angular 1.2.0 中失败,但在 1.
- 如何在 angularjs 茉莉花单元测试中正确引用服务
- 无法将服务注入其 Angular 单元测试中
- 角度单元测试服务元素持久性
- 在 Karma/Jasmine 单元测试中注入 Angular $timeout服务的真实实现
- 尝试在 AngularJS 上对基本服务进行单元测试时出错
- 单元测试依赖于服务的角度过滤器
- 类型错误:parsed 在 angularjs 服务单元测试中未定义
- 如何在Jasmine中对依赖服务的函数进行单元测试
- 单元测试服务不;t注入工厂
- 为创建Json REST服务的模块编写单元测试
- 如何对一个主要负责重定向到应用程序外部页面的Angular服务进行单元测试
- 错误:单元测试AngularJs服务时,没有待处理的flush请求
- 单元测试调用服务的指令
- 用注入服务对angular控制器进行单元测试
- Angular单元测试,模拟使用$injector注入的服务
- Angularjs服务单元测试