axe*_*uch 3 javascript unit-testing mocking jasmine angularjs
我正在使用karmawith 编写一些服务的测试jasmine,我想知道是否必须模拟使用的服务依赖项$http,如下所述.
PS:我已经$httpBackend用来模拟任何GET请求,$httpBackend.expect*如果我不嘲笑服务,我打算使用它ApiProvider
.factory('CRUDService', ['ApiProvider', function (ApiProvider) {
'use strict';
var CRUD = function CRUD(modelName) {
this.getModelName = function () {
return modelName;
};
},
overridableMethods = {
save: null
};
CRUD.prototype = {
save: function () {
// ABSTRACT
},
/**
* Deletes instance from id property
* @return http promise
*/
remove: function () {
return ApiProvider.delete(this.getModelName(), this.id);
}
};
return {
/**
* Function creating a class extending CRUD
* @param {string} modelName
* @param {Object} methods an object with methods to override, ex: save
* return {classD} the extended class
*/
build: function (modelName, methods) {
var key,
Model = function () {
};
// Class extending CRUD
Model.prototype = new CRUD(modelName);
// Apply override on methods allowed for override
for (key in methods) {
if (key in overridableMethods &&
typeof methods[key] === 'function') {
Model.prototype[key] = methods[key];
}
}
/**
* Static method
* Gets an entity of a model
* @param {Object} config @see ApiProvider config
* @return {CRUD} the reference to the entity
*/
Model.get = function (config, success) {
var entity = new Model();
ApiProvider.get(modelName, config)
.success(function (data) {
angular.extend(entity, data);
if (success) {
success();
}
});
return entity;
};
/**
* Static method
* Gets entities of a model
* @param {Object} config @see ApiProvider config
* @return {CRUD[]} the reference to the entity
*/
Model.query = function (config, success) {
var entities = [];
ApiProvider.get(modelName, config)
.success(function (data) {
data.map(function (model) {
var entity = new Model();
angular.extend(entity, model);
return entity;
});
Array.prototype.push.apply(entities, data);
if (success) {
success();
}
});
return entities;
};
return Model;
},
// Direct link to ApiProvider.post method
post: ApiProvider.post,
// Direct link to ApiProvider.put method
put: ApiProvider.put
};
}]);
Run Code Online (Sandbox Code Playgroud)
ApiProvider.service('ApiProvider', function ($http) {
/**
* Private
* @param {string}
* @param {object}
* @return {string} Example: /service/[config.id[/config.relatedModel], /?config.params.key1=config.params.value1&config.params.key2=config.params.value2]
*/
var buildUrl = function (service, config) {
var push = Array.prototype.push,
url = [apiRoot, service],
params = [],
param = null;
// if a key id is defined, we want to target a specific resource
if ('id' in config) {
push.apply(url, ['/', config.id]);
// a related model might be defined for this specific resource
if ('relatedModel' in config) {
push.apply(url, ['/', config.relatedModel]);
}
}
// Build query string parameters
// Please note that you can use both an array or a string for each param
// Example as an array:
// {
// queryString: {
// fields: ['field1', 'field2']
// }
// }
// Example as a string
// {
// queryString: {
// fields: 'field1,field2'
// }
// }
if ('queryString' in config) {
// loop through each key in config.params
for (paramName in config.queryString) {
// this gives us something like [my_key]=[my_value]
// and we push that string in params array
push.call(params, [paramName, '=', config.queryString[paramName]].join(''));
}
// now that all params are in an array we glue it to separate them
// so that it looks like
// ?[my_first_key]=[my_first_value]&[my_second_key]=[my_second_value]
push.apply(url, ['?', params.join('&')]);
}
return url.join('');
},
request = function (method, url, methodSpecificArgs) {
trace({
method: method,
url: url,
methodSpecificArgs: methodSpecificArgs
}, 'ApiProvider request');
return $http[method].apply($http, [url].concat(methodSpecificArgs));
},
methods = {
'get': function (url, config) {
config.cache = false;
return request('get', url, [config]);
},
'post': function (url, data, config) {
config.cache = false;
return request('post', url, [data, config]);
},
'put': function (url, data, config) {
config.cache = false;
return request('put', url, [data, config]);
},
'delete': function (url, config) {
config.cache = false;
return request('delete', url, [config]);
}
};
return {
'get': function (service, config) {
config = config || {};
return methods.get(buildUrl(service, config), config);
},
'post': function (service, data, config) {
config = config || {};
return methods.post(buildUrl(service, config), data, config);
},
'put': function (service, data, config) {
config = config || {};
return methods.put(buildUrl(service, config), data, config);
},
'delete': function (service, config) {
config = config || {};
return methods.delete(buildUrl(service, config), config);
}
};
});
Run Code Online (Sandbox Code Playgroud)
describe('CRUDServiceTest', function () {
'use strict';
var CRUDService;
beforeEach(function () {
inject(function ($injector) {
CRUDService = $injector.get('CRUDService');
});
});
it('should have a method build', function () {
expect(CRUDService).toHaveMethod('build');
});
it('should ensure that an instance of a built service has a correct value for getModelName method',
function () {
var expectedModelName = 'myService',
BuiltService = CRUDService.build(expectedModelName),
instanceOfBuiltService = new BuiltService();
expect(instanceOfBuiltService).toHaveMethod('getModelName');
expect(instanceOfBuiltService.getModelName()).toEqual(expectedModelName);
});
// TEST get
it('should ensure build returns a class with static method get', function () {
expect(CRUDService.build()).toHaveMethod('get');
});
it('should ensure get returns an instance of CRUD', function() {
var BuiltService = CRUDService.build(),
instanceOfBuiltService = new BuiltService();
expect((BuiltService.get()).constructor).toBe(instanceOfBuiltService.constructor);
});
// TEST query
it('should ensure build returns a class with static method query', function () {
expect(CRUDService.build()).toHaveMethod('query');
});
it('should a collection of CRUD', function () {
expect(CRUDService.build()).toHaveMethod('query');
});
it('should have a static method post', function () {
expect(CRUDService).toHaveMethod('post');
});
it('should have a static method put', function () {
expect(CRUDService).toHaveMethod('put');
});
});
Run Code Online (Sandbox Code Playgroud)
TLDR;
总的来说,我认为嘲笑你的服务是个好主意.如果你继续这样做,那么它会使你添加的任何服务的行为变得非常容易.
话虽这么说,你根本不需要,你可以简单地使用Jasmine间谍.
例如,如果您正在测试具有如下方法的CRUDService:
remove: function () {
return ApiProvider.delete(this.getModelName(), this.id);
}
Run Code Online (Sandbox Code Playgroud)
您可以在测试中写下以下内容:
var spy = spyOn(ApiProvider, 'delete').andCallFake(function(model, id) {
var def = $q.defer();
$timeout(function() { def.resolve('something'); }, 1000)
return def.promise;
});
Run Code Online (Sandbox Code Playgroud)
如果你打电话给它:
var promise = CRUDService.remove();
expect(ApiProvider.delete).toHaveBeenCalledWith(CRUDService.getModelName(), CRUDService.id);
Run Code Online (Sandbox Code Playgroud)
因此,基本上您可以模拟测试中所需的功能,而无需完全模拟服务.你可以在这里阅读更多内容
希望这有帮助!