[英]Jasmine unit tests not waiting for promise resolution
我有一個像這樣的異步依賴的角度服務
(function() {
angular
.module('app')
.factory('myService', ['$q', 'asyncService',
function($q, asyncService) {
var myData = null;
return {
initialize: initialize,
};
function initialize(loanId){
return asyncService.getData(id)
.then(function(data){
console.log("got the data!");
myData = data;
});
}
}]);
})();
我想單元測試initialize
函數,我正在嘗試像這樣的茉莉:
describe("Rate Structure Lookup Service", function() {
var $q;
var $rootScope;
var getDataDeferred;
var mockAsyncService;
var service;
beforeEach(function(){
module('app');
module(function ($provide) {
$provide.value('asyncService', mockAsyncService);
});
inject(function(_$q_, _$rootScope_, myService) {
$q = _$q_;
$rootScope = _$rootScope_;
service = myService;
});
getDataDeferred = $q.defer();
mockAsyncService = {
getData: jasmine.createSpy('getData').and.returnValue(getDataDeferred.promise)
};
});
describe("Lookup Data", function(){
var data;
beforeEach(function(){
testData = [{
recordId: 2,
effectiveDate: moment("1/1/2015", "l")
},{
recordId: 1,
effectiveDate: moment("1/1/2014", "l")
}];
});
it("should get data", function(){
getDataDeferred.resolve(testData);
service.initialize(1234).then(function(){
console.log("I've been resolved!");
expect(mockAsyncService.getData).toHaveBeenCalledWith(1234);
});
$rootScope.$apply();
});
});
});
沒有任何控制台消息出現,測試似乎只是在沒有承諾得到解決的情況下繼續進行。 我雖然$rootScope.$apply()
會這樣做,但似乎沒有。
UPDATE
@estus是對的$rootScope.$appy()
足以觸發所有承諾的解決。 似乎問題出在我對asyncService的模擬中。 我把它改成了
mockAsyncService = {
getData: jasmine.createSpy('getData').and.returnValue(getDataDeferred.promise)
};
至
mockAsyncService = {
getData: jasmine.createSpy('getData').and.callFake(
function(id){
return $q.when(testData);
})
};
我將testData
設置為我需要的測試,而不是調用getDataDeferred.resolve(testData)
。 這種變化之前,mockAsyncService被注射但是對於承諾getDataDeferred
從來沒有得到解決。
我不知道這是否是在beforeEach
中注入的beforeEach
或什么。 更奇怪的是,這必須是一個callFake
。 使用.and.returnValue($q.when(testData))
仍會阻止承諾解析。
角度承諾在測試期間是同步的, $rootScope.$apply()
足以使它們在規范結束時結算。
除非asyncService.getData
返回一個真正的promise而不是$q
promise(在這種情況下它不會),asynchronicity在Jasmine中不是問題。
Jasmine promise matchers庫非常適合測試Angular的承諾。 除了明顯缺乏冗長之外,它還在這種情況下提供了有價值的反饋。 雖然這個
rejectedPromise.then((result) => {
expect(result).toBe(true);
});
規范將在不應該的時候通過,這個
expect(pendingPromise).toBeResolved();
expect(rejectedPromise).toBeResolvedWith(true);
將失敗的有意義的消息。
測試代碼的實際問題在beforeEach
是優先的。 角度自舉過程不同步。
getDataDeferred = $q.defer()
應該放入inject
塊,否則它將在模塊被引導並且注入$q
之前執行。 同樣的擔憂mockAsyncService
使用getDataDeferred.promise
。
在最好的情況下,代碼會拋出錯誤,因為在undefined
調用了defer
方法。 在最壞的情況下(這就是為什么這樣的規范屬性this.$q
比本地套件變量更可取的原因) $q
屬於前一個規范的一個注入器,因此$rootScope.$apply()
在這里沒有效果。
您需要將可選的done參數傳遞給it塊中的回調函數。 否則jasmine無法知道你正在測試異步函數 - 異步函數會立即返回。
這是重構:
it("should get data", function(done){
service.initialize(1234).then(function(){
console.log("I've been resolved!");
expect(mockAsyncService.getData).toHaveBeenCalledWith(1234);
done();
});
});
這里有一些(片狀,背面的啤酒墊)指針。 不幸的是,我無法知道它們是否是實際錯誤,或者它們是否是“拼寫錯誤”,因為您“簡化”了代碼。
首先,沒有理由不將asyncService作為服務和內聯提供。 試試這個:
$provide.service('asyncService', function() {
// asyncService implementation
});
另外,我不相信這種依賴注入會起作用。
inject(function(_$q_, _$rootScope_, myService) {
$q = _$q_;
$rootScope = _$rootScope_;
service = myService;
});
因為DI容器不知道myServiceProvider。 你可以試試這個:
inject(function(_$q_, _$rootScope_, _asyncService_) {
$q = _$q_;
$rootScope = _$rootScope_;
service = _asyncService_;
});
哪個會起作用,因為您之前使用'asyncService'作為參數調用$ provide。
另外,你沒有正確使用$ promise api。 你沒有在單元測試中向.then()返回resolve()'s promise。 嘗試使用asyncService的替代實現,類似於:
$provide.service('asyncService', function() {
this.getData = function() {
return $q(function(resolve, reject) {
resolve('Promise resolved');
});
}
});
你可以在你的單元測試中監視這個。 沒有理由在beforeEach()函數中調用間諜。
jasmine.spyOn(service, 'getData').and.callThrough();
你的期望()看起來不錯。
如果有任何這些對你有幫助,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.