如何模拟一个角度 $http 调用并返回一个行为类似于 $http 的 promise 对象
Posted
技术标签:
【中文标题】如何模拟一个角度 $http 调用并返回一个行为类似于 $http 的 promise 对象【英文标题】:How to mock an angular $http call and return a promise object that behaves like $http 【发布时间】:2014-08-14 22:18:59 【问题描述】:有没有办法返回一个 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”的修订版(sn-p),在 promise 对象上包含 success
和 error
。但是,我该如何执行success
方法呢?
return
get : function(itemId)
if (isInTestingMode)
var promise = $.defer().promise;
// Mimicking $http.get's success
promise.success = function(fn)
promise.then(function()
fn( itemId : "123", name : "ItemName", 200, , );
);
return promise;
;
// Mimicking $http.get's error
promise.error = function(fn)
promise.then(null, function(response)
fn("Error", 404, , );
);
return promise;
;
return promise;
return $http.get("/myapp/items/" + itemId);
【问题讨论】:
【参考方案1】:只需使用$q
service 的deferred
方法
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 your fake $http call")
return deferred.promise
然后你可以像$http
承诺一样调用你的函数(当然,你必须自定义你想要放入其中的任何内容)。
fakeHttpCall(true).then(
function (data)
// success callback
console.log(data)
,
function (err)
// error callback
console.log(err)
)
【讨论】:
我不明白如何强制执行成功或错误。我根据您的评论使用扩展示例更新了我的问题。我看不出如何从 MyService 的“get”中强制使用成功方法。 如果你想模拟成功,你只需要设置deferred.resolve(result)
然后返回promise ....或者如果你想模拟失败,设置deferred.reject(error)
我很确定这不会起作用,因为deferred.promise
不提供success()
和error()
用于链接,例如$http。例如,请参阅此答案以了解如何实现这一目标 - ***.com/a/19747182/404099。
@IliaBarahovski success()
和 error()
不再使用。 Promises 为您提供then( success, error )
构造,它更强大,可以链接到无限。见docs.angularjs.org/api/ng/service/$q
@IliaBarahovski 你是完全正确的,我说得对,但写错了:)【参考方案2】:
我发现this post 与我所问的类似。
但是,我想要一种方法来模拟我的服务调用,以便可以返回虚假数据,而不是发出真正的 HTTP 请求调用。对我来说,处理这种情况的最好方法是使用 angular 的$httpBackend
服务。例如,要绕过对我的“项目”资源的 GET 请求但不绕过我的部分/模板的 GET,我会这样做:
angular
.module('myApp', ['ngMockE2E'])
.run(['$httpBackend', function($httpBackend)
$httpBackend
.whenGET(/^partials\/.+/)
.passThrough();
$httpBackend
.whenGET(/^\/myapp\/items\/.+/)
.respond(itemId : "123", name : "ItemName");
]);
有关 $httpBackend 的更多信息,请参阅this documentation。
【讨论】:
【参考方案3】:我终于found a way using jasmin。 $httpBackend
对我来说是没有选择的,因为我还需要在同一服务上模拟非 $http 方法。我还认为需要指定 url 的控制器测试并不完美,因为控制器和它的测试不需要知道它。
这是它的工作原理:
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;
;
然后在你的代码中,你会从 promise 中返回一个新的 fakeHttp。
if(testingMode)
return new FakeHttp(promise);
;
promise 必须是异步的,否则将不起作用。为此,您可以使用 $timeout。
【讨论】:
【参考方案5】:简单易懂!
您可以像这样使用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;
);
]);
【讨论】:
以上是关于如何模拟一个角度 $http 调用并返回一个行为类似于 $http 的 promise 对象的主要内容,如果未能解决你的问题,请参考以下文章