我有一个包含这样的资源工厂的服务:
serviceModule.factory('ProjectResource', ['$resource', function($resource){
return $resource('/projects/:id.json', {}, {
'query': {method: 'GET', isArray: true}}
);
}]);
在一个驻留在控制器中的表单中,我注入了serviceModule,并创建了一个资源对象的新实例:
$scope.project = new ProjectResource({name: 'Enter a name'})
我有一些嘲弄它的问题。我尝试创建这样的模拟对象,并将其注入控制器:
mockProjectResource = {
query: function(){
deferred = $q.defer();
deferred.resolve({id: 1, :name:'test'});
return deferred.promise;
}
};
无论单元测试,我都会收到错误:
TypeError: undefined is not a function
哪个指向Project Resource对象的初始化($scope.project = new ProjectResource({name: 'Enter a name'})
)。
有没有什么好方法来嘲笑 new ProjectResource(...)
?
从我所看到的,我只需要创建一个存根对象而不是一个模拟对象。
如 ProjectResource
是一个 构造函数 功能。您可以像这样创建它的存根:
mockProjectResource = function (properties){
for(var k in properties)
this[k]=properties[k];
};
mockProjectResource.query = function(){
deferred = $q.defer();
deferred.resolve({id: 1, :name:'test'});
return deferred.promise;
};
如果您需要知道模拟和存根之间的差异: Rhino Mocks上的模拟和存根之间有什么区别?。 (不要认为这个链接仅适用于Rhino mock,这个概念对所有框架和技术都是一样的)
从我所看到的,我只需要创建一个存根对象而不是一个模拟对象。
如 ProjectResource
是一个 构造函数 功能。您可以像这样创建它的存根:
mockProjectResource = function (properties){
for(var k in properties)
this[k]=properties[k];
};
mockProjectResource.query = function(){
deferred = $q.defer();
deferred.resolve({id: 1, :name:'test'});
return deferred.promise;
};
如果您需要知道模拟和存根之间的差异: Rhino Mocks上的模拟和存根之间有什么区别?。 (不要认为这个链接仅适用于Rhino mock,这个概念对所有框架和技术都是一样的)
你看过了吗? 'ngMock'?
angular.module('project-resource', ['ngResource'])
.factory('ProjectResource', function($resource){
//Do not need to redefine the `query` action as it is defined by default by ngResource
return $resource('/projects/:id.json', {'id': '@id'});
});
describe('ProjectResource', function(){
beforeEach(module('service-module-name'));
afterEach(inject(function($httpBackend){
//These two calls will make sure that at the end of the test, all expected http calls were made
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('mock http call', inject(function($httpBackend, ProjectResource) {
var resource = new ProjectResource({
id : 'abcde'
});
//Create an expectation for the correct url, and respond with a mock object
$httpBackend.expectGET('/projects/abcde.json').respond(200, JSON.stringify({
id : 'abcde',
name : 'test'
}));
//Make the query
resource.$query();
//Because we're mocking an async action, ngMock provides a method for us to explicitly flush the request
$httpBackend.flush();
//Now the resource should behave as expected
expect(resource.name).toBe('test');
}));
});
我创建了一个小型库来帮助模拟ngResource: https://github.com/michalstawski/ngResourceMock
它与$ httpBackend具有类似的api。有了它你可以在你的测试中做这样的事情:
$scope.project.whenQuery().resolve([]);
这将解析每个使用空数组查询的调用。
它比自己模拟资源更容易,更快,同时在比httpBackend更高的抽象级别上。
模拟基于资源的服务的另一种方法是这样的:
// the name of an object should start with a capital letter
// in the case where we create a function object (as opposed to
// a literal object).
MockProjectResource = function(){};
// we add the "query" method to the "prototype" property
MockProjectResource.prototype.query = function(){
deferred = $q.defer();
deferred.resolve({id: 1, :name:'test'});
return deferred.promise;
};
模拟上面的查询函数适用于控制器期望通过调用返回promise的情况,例如:
var projectController = angular.module('projectController', []);
projectController.controller('ProjectController', ['$scope', 'ProjectResource',
function($scope, ProjectResource) {
var promise = ProjectResource.query();
promise.then(function(data) {
console.log(data);
},
function(err){
console.log(err);
});
}]);
但是,如果控制器期望在回调中发送结果,如下所示:
ProjectResource.query(function(data) {
console.log(data);
},
function(err){
console.log(err);
});
那么,服务需要像这样嘲笑:
MockProjectResource.prototype.query = function(success, error){
var deferred = q.defer();
var promise = deferred.promise;
promise.then(success, error);
deferred.resolve({id: 1, :name:'test'});
};
有关完整示例,请参阅我对类似问题的回答:
你如何模拟angularjs $资源工厂