Как издеваться над сервисом, содержащим ресурс в AngularJS
У меня есть служба, которая содержит ресурс factory следующим образом:
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;
}
};
Независимо от unit test, я получаю ошибку:
TypeError: undefined is not a function
Что указывает на инициализацию объекта Project Resource ($scope.project = new ProjectResource({name: 'Enter a name'})
).
Есть ли хороший способ издеваться над new ProjectResource(...)
?
Ответы
Ответ 1
Из того, что я вижу, мне нужно только создать объект-заглушку вместо макет-объекта.
Как 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;
};
Если вам нужно знать различия между mocks и stubs: В чем разница между mocks и stubs в Rhino Mocks?. (Не думайте, что эта ссылка предназначена только для Rhino mock, концепция означает то же самое для всех фреймворков и технологий)
Ответ 2
Вы видели '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');
}));
});
Ответ 3
Я создал небольшую библиотеку, чтобы помочь насмешка ngResource: https://github.com/michalstawski/ngResourceMock
Он похож на api, как $httpBackend. С его помощью вы могли бы сделать что-то подобное в своем тесте:
$scope.project.whenQuery().resolve([]);
Это разрешит каждый вызов запроса с пустым массивом.
Это проще и быстрее, чем насмехаться над этим ресурсом, и в то же время превышать уровень абстракции выше $httpBackend.
Ответ 4
Другой способ обмануть службу на основе ресурсов выглядит следующим образом:
// 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;
};
Смыкая функцию запроса, как указано выше, работает в том случае, когда контроллер ожидает, что обещание будет возвращено в результате вызова, например:
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 $ factory