我想测试这个功能:
function initializeView() {
var deferred = $q.defer();
if(this.momentArray) {
core.listMoments(constants.BEST_MOMENT_PREFIX, '').then(function(moments) {
//Ommitted
deferred.resolve(moments);
}, function(error) {
console.log("ERROR");
deferred.reject(error);
});
}
else {
deferred.resolve();
}
return deferred.promise;
};
该函数调用core.listMoments:
function listMoments(prefix, startAfter) {
// var deferred = $q.defer();
var promises = [];
return awsServices.getMoments(prefix, startAfter).then(function(moments) { //Mocked
console.log("getMoments Returned"); //Does not print
for(var i = 0; i < moments.length; i++) {
// moments[i].Key = constants.IMAGE_URL + moments[i].Key;
promises.push(getMomentMetaData(moments[i]));
}
return $q.all(promises);
});
};
这是我的测试功能:
it('Should correctly initialize the view', function(done) {
spyOn(awsServices, 'getMoments').and.callFake(function() {
console.log("getMoments Has been mocked"); //This prints
return $q.resolve(mock_moment);
});
service.initializeView().then(function() {
done();
})
});
问题在于awsServices的'getMoments'模拟。对awsServices.getMoments的调用位于listMoments函数中。我想模仿这个函数,但是当我这样做时,它不会执行promise的“then”部分。
因此,基于我的控制台日志,它将打印'getMoments Has mocked'日志,但它不会打印'getMoments Returned'日志。因此该函数被模拟,但由于某种原因,它没有进入then语句,我的测试只是超时。
答案 0 :(得分:3)
为了让承诺的.then()
部分在这样的测试中工作,您需要使用$rootScope.$apply()
。无论promise是在您的测试代码中还是在正在测试的引用库中,都需要这样做。可以将其视为flush()
或$http
调用的$timeout
函数。
Angular documentation's $q page中的测试示例显示了如何使用它:
it('should simulate promise', inject(function($q, $rootScope) {
var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).toBeUndefined();
// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).toBeUndefined();
// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
}));
请注意,他们会注入$rootScope
。
答案 1 :(得分:1)
$q
promises可以是同步的(当它们被同步解析时)并依赖于摘要周期。
在Angular测试中通常应该没有异步done
回调。
角度测试应该是同步的,$q
承诺也是如此。为了实现这一点,当现有的承诺(从getMoments
和initializeView
返回的承诺)与then
链接时,应手动触发摘要。如果在done
内放置了then
回调并且未触发摘要,则会导致规范超时。
spyOn(awsServices, 'getMoments').and.callFake(function() {
console.log("getMoments Has been mocked"); //This prints
return $q.resolve(mock_moment);
});
service.initializeView();
$rootScope.$digest();
这里可以改进的是隔离。单个测试中涉及多个单元(方法)。当其中一个失败时,这将影响故障排除。
通常,单元测试意味着每次只测试一个单元,而其余单元则被模拟或存根。在这种情况下,在一个测试中service.listMoments
被调用并且awsServices.getMoments
被模拟,而在另一个测试中service.initializeView
被调用并且service.listMoments
被模拟。