我目前正在使用karma
和jasmine
编写某些服务的测试,我想知道是否必须使用$http
来模拟服务的服务依赖项,如下所述。 / p>
PS :我已经使用$httpBackend
来模拟任何GET请求,如果我不模拟服务,我打算使用$httpBackend.expect*
{{1} }
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
};
}]);
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);
}
};
});
TLDR;
答案 0 :(得分:5)
总的来说,我认为嘲笑你的服务是个好主意。如果你继续这样做,那么它就可以很容易地隔离你添加的任何服务的行为。
话虽如此,你根本不需要,你可以简单地使用Jasmine间谍。
例如,如果您正在测试具有以下方法的CRUDService:
remove: function () {
return ApiProvider.delete(this.getModelName(), this.id);
}
你可以在你的测试中写下类似的东西:
var spy = spyOn(ApiProvider, 'delete').andCallFake(function(model, id) {
var def = $q.defer();
$timeout(function() { def.resolve('something'); }, 1000)
return def.promise;
});
然后如果你打电话:
var promise = CRUDService.remove();
expect(ApiProvider.delete).toHaveBeenCalledWith(CRUDService.getModelName(), CRUDService.id);
基本上,您可以在测试中模拟出所需的功能,而无需完全模拟服务。您可以阅读更多here
希望这有帮助!