Как высмеивать вызов angular $http и возвращать объект обещания, который ведет себя как $http
Есть ли способ вернуть HttpPromise (или что-то подобное), чтобы имитировать вызов $http
? Я хочу установить глобальную переменную, которая указывает, был ли выполнен настоящий HTTP-запрос, или возвращается ли поддельный объект HttpPromise поддельными данными.
Например, у меня есть служба, которая похожа на это:
angular
.module('myservice')
.factory('MyService', ['$http', function($http) {
return {
get : function(itemId) {
if (isInTestingMode) {
// return a promise obj that returns success and fake data
}
return $http.get("/myapp/items/" + itemId);
}
};
} ]);
И в моем контроллере у меня есть вызов вышеупомянутой службы, которая выглядит примерно так:
// Somewhere in my controller
MyService.get($scope.itemId)
.success(function(data) {
$scope.item = data;
})
.error(function(data, status, headers, config) {
$scope.notFound = true;
});
Я пытаюсь изменить не код контроллера; Я хочу, чтобы цепочка success
и error
продолжала работать, когда в моем "isInTestMode".
Можно ли подделать HttpPromise
так, как я описал в сервисе?
Ниже приведено пересмотренное издание "MyService" выше (фрагмент), содержащий success
и error
на объекте обещания. Но как мне выполнить метод success
?
return {
get : function(itemId) {
if (isInTestingMode) {
var promise = $.defer().promise;
// Mimicking $http.get success
promise.success = function(fn) {
promise.then(function() {
fn({ itemId : "123", name : "ItemName"}, 200, {}, {});
});
return promise;
};
// Mimicking $http.get error
promise.error = function(fn) {
promise.then(null, function(response) {
fn("Error", 404, {}, {});
});
return promise;
};
return promise;
}
return $http.get("/myapp/items/" + itemId);
}
}
Ответы
Ответ 1
Я обнаружил, что этот пост похож на то, что я просил.
Тем не менее, мне нужен способ издеваться над моим сервисным вызовом, чтобы можно было вернуть ложные данные вместо того, чтобы выдавать истинный запрос HTTP-запроса. Лучший способ справиться с этой ситуацией, для меня - использовать службу angular $httpBackend
. Например, чтобы обойти запрос GET для моего ресурса "items", но чтобы не обходить GET моих партикулов/шаблонов, я бы сделал что-то вроде этого:
angular
.module('myApp', ['ngMockE2E'])
.run(['$httpBackend', function($httpBackend) {
$httpBackend
.whenGET(/^partials\/.+/)
.passThrough();
$httpBackend
.whenGET(/^\/myapp\/items\/.+/)
.respond({itemId : "123", name : "ItemName"});
}]);
Подробнее о $httpBackend см. эту документацию.
Ответ 2
Просто используйте метод deferred
службы $q
var fakeHttpCall = function(isSuccessful) {
var deferred = $q.defer()
if (isSuccessful === true) {
deferred.resolve("Successfully resolved the fake $http call")
}
else {
deferred.reject("Oh no! Something went terribly wrong in you fake $http call")
}
return deferred.promise
}
И тогда вы можете назвать свою функцию как обещание $http
(вам нужно, конечно, настроить все, что вы хотите поместить внутри нее).
fakeHttpCall(true).then(
function (data) {
// success callback
console.log(data)
},
function (err) {
// error callback
console.log(err)
})
Ответ 3
Наконец, нашел способ использования jasmin. $httpBackend
для меня не было выбора, так как были также не $http-методы, которые мне нужны были в одной службе. Я также думаю, что проверка контроллера, требующая указания URL-адреса, не идеальна, поскольку imho контроллер и его тест не должны знать об этом.
Вот как это работает:
beforeEach(inject(function ($controller, $rootScope, $q) {
scope = $rootScope.$new();
mockSvc = {
someFn: function () {
},
someHttpFn: function () {
}
};
// use jasmin to fake $http promise response
spyOn(mockSvc, 'someHttpFn').and.callFake(function () {
return {
success: function (callback) {
callback({
// some fake response
});
},
then: function(callback) {
callback({
// some fake response, you probably would want that to be
// the same as for success
});
},
error: function(callback){
callback({
// some fake response
});
}
}
});
MyCtrl = $controller('MyCtrl', {
$scope: scope,
MyActualSvc: mockSvc
});
}));
Ответ 4
Вы можете реализовать свой класс FakeHttp:
var FakeHttp = function (promise) {
this.promise = promise;
this.onSuccess = function(){};
this.onError = function(){};
this.premise.then(this.onSuccess, this.onError);
};
FakeHttp.prototype.success = function (callback) {
this.onSuccess = callback;
/**You need this to avoid calling previous tasks**/
this.promise.$$state.pending = null;
this.promise.then(this.onSucess, this.onError);
return this;
};
FakeHttp.prototype.error = function (callback) {
this.onError = callback;
/**You need this to avoid calling previous tasks**/
this.promise.$$state.pending = null;
this.promise.then(this.onSuccess, this.onError);
return this;
};
Затем в вашем коде вы должны вернуть новый fakeHttp из обещания.
if(testingMode){
return new FakeHttp(promise);
};
Обещание должно быть асинхронным, иначе это не сработает. Для этого вы можете использовать $timeout.
Ответ 5
легкий peasy!
Вы можете сделать это, используя angular-mocks-async следующим образом:
var app = ng.module( 'mockApp', [
'ngMockE2E',
'ngMockE2EAsync'
]);
app.run( [ '$httpBackend', '$q', function( $httpBackend, $q ) {
$httpBackend.whenAsync(
'GET',
new RegExp( 'http://api.example.com/user/.+$' )
).respond( function( method, url, data, config ) {
var re = /.*\/user\/(\w+)/;
var userId = parseInt(url.replace(re, '$1'), 10);
var response = $q.defer();
setTimeout( function() {
var data = {
userId: userId
};
response.resolve( [ 200, "mock response", data ] );
}, 1000 );
return response.promise;
});
}]);