Jasmine 不会测试具有多个嵌套承诺的 angular 服务
Jasmine won't test an angular service with multiple nested promises
我正在尝试为 angular 服务编写 jasmine 测试用例。因为我必须调用 PouchDB 并获取 http 资源,所以我使用 $q。
在我的应用程序中,我有两个数据源:作为 localStorage 的 PouchDB 和一个通过 http 请求的 XML 文档。我成功地为 localStorage.service 和 XMLparser.service 编写了测试,没有遇到太多麻烦(我必须执行 50 毫秒的超时,然后调用 $rootScopre.$digest())。
然而,当我尝试 运行 测试使用 localStorage 和 XMLParser 的服务时,我就是无法通过。 Karma 挂起 5 秒,测试在超时时失败。如果我在任何地方添加一些 console.log() ,所有代码都会被实际执行。就像 done() 没有关闭测试用例一样。如果我 运行 代码直接在我的应用程序中运行。
我的代码中发生了很多事情,所以我会尝试解释一下:
function loadCategories(){
var deferred = $q.defer();
//Json structure that tells my parser what to extract from the XML
var structure = { ... }
//Check in the PouchDB to see if the structure is already cached
localStorage.getStructure()
.then(function(struct){
deferred.resolve(struct.category);
})
.catch(function(e){
if(e.name != 'not_found') return deferred.reject(e);
//If the structure is not cached, get it from the parser through an http request
xmlparser.readFile(apiUrl, structure)
.then(function(doc){
localStorage.saveStructure(doc); //Save it in the PouchDB
deferred.resolve(doc.category); //Resolve with the categories
})
.catch(function(e){
deferred.reject(e);
});
});
return deferred.promise;
}
以下是我的测试方式:
it('should list the categories', function(done){
//The answer to this call is mocked in the beforeEach
$httpBackend.expectGET('/lib/xmlDocuments/indicator.xml');
var promise = Indicator.getCategories()
.then(function(categories){
expect(categories[0].name).toBe('Emplois par secteur d\'activité');
expect(categories[1].name).toBe('Emplois par niveau de compétence');
done();
//When i put a console.log here it get executed...
})
.catch(function(e){
expect(e).toBe(null);
});
//Here i tryed to force a $digest and then flush() the requests
//but i still get "Async callback was not invoked within timeout"
setTimeout(function() {
$scope.$digest();
setTimeout(function(){
$httpBackend.flush();
}, 50);
}, 50);
});
现在我很确定有些事情我做得不对,但我不能指责它:
- 我是不是用错了承诺?
- 我应该模拟 XMLParser 和 localStorage 吗?
- 我的茉莉花有没有错误?
- 我是否过度使用 $q?
感谢您的帮助!
好吧,我最终模拟了 XMLParser 和 localStorage。我得出的结论是,再次重新测试这些服务是没有意义的。这是一个单元测试,应该只测试一个单元。
我还按照 Explosion Pills 的建议修改了我对 promises 的使用。
//Load the categories
function loadCategories(){
var structure = { ... }
return localStorage.getStructure()
.then(function(struct){
return struct.category;
})
.catch(function(e){
if(e.name != 'not_found') return e;
return xmlparser.readFile(apiUrl, structure)
.then(function(doc){
localStorage.saveStructure(doc);
return doc.category;
});
});
}
我像以前一样添加了模拟
beforeEach(module('gisMobile', function($provide){
xmlParserMock = {
readFile: function(){
var defer = $q.defer();
defer.resolve(indicatorJsonMock);
return defer.promise;
}
};
localStorageMock = {
isStructureCached: false,
getStructure: function(){
var defer = $q.defer();
if(localStorageMock.isStructureCached)
defer.resolve(indicatorJsonMock);
else
defer.reject({name: 'not_found'});
return defer.promise;
},
saveStructure: function(){
localStorageMock.isStructureCached = true;
}
}
$provide.value('xmlparser', xmlParserMock);
$provide.value('localStorage', localStorageMock);
}));
最终测试现在看起来像
it('should list the categories', function(done){
var promise = Indicator.getCategories()
.then(function(categories){
expect(categories[1].name).toBe('Emplois par secteur d\'activité');
expect(categories[0].name).toBe('Emplois par niveau de compétence');
done();
})
.catch(function(e){
expect(e).toBe(null);
});
$rootScope.$digest();
});
所以为了解决这个问题,我的假设是正确的:
- 我没有正确使用承诺
- 我不得不模拟 xmlParser 和 localStorage,因为它们不是本次测试的目标。
我正在尝试为 angular 服务编写 jasmine 测试用例。因为我必须调用 PouchDB 并获取 http 资源,所以我使用 $q。
在我的应用程序中,我有两个数据源:作为 localStorage 的 PouchDB 和一个通过 http 请求的 XML 文档。我成功地为 localStorage.service 和 XMLparser.service 编写了测试,没有遇到太多麻烦(我必须执行 50 毫秒的超时,然后调用 $rootScopre.$digest())。
然而,当我尝试 运行 测试使用 localStorage 和 XMLParser 的服务时,我就是无法通过。 Karma 挂起 5 秒,测试在超时时失败。如果我在任何地方添加一些 console.log() ,所有代码都会被实际执行。就像 done() 没有关闭测试用例一样。如果我 运行 代码直接在我的应用程序中运行。
我的代码中发生了很多事情,所以我会尝试解释一下:
function loadCategories(){
var deferred = $q.defer();
//Json structure that tells my parser what to extract from the XML
var structure = { ... }
//Check in the PouchDB to see if the structure is already cached
localStorage.getStructure()
.then(function(struct){
deferred.resolve(struct.category);
})
.catch(function(e){
if(e.name != 'not_found') return deferred.reject(e);
//If the structure is not cached, get it from the parser through an http request
xmlparser.readFile(apiUrl, structure)
.then(function(doc){
localStorage.saveStructure(doc); //Save it in the PouchDB
deferred.resolve(doc.category); //Resolve with the categories
})
.catch(function(e){
deferred.reject(e);
});
});
return deferred.promise;
}
以下是我的测试方式:
it('should list the categories', function(done){
//The answer to this call is mocked in the beforeEach
$httpBackend.expectGET('/lib/xmlDocuments/indicator.xml');
var promise = Indicator.getCategories()
.then(function(categories){
expect(categories[0].name).toBe('Emplois par secteur d\'activité');
expect(categories[1].name).toBe('Emplois par niveau de compétence');
done();
//When i put a console.log here it get executed...
})
.catch(function(e){
expect(e).toBe(null);
});
//Here i tryed to force a $digest and then flush() the requests
//but i still get "Async callback was not invoked within timeout"
setTimeout(function() {
$scope.$digest();
setTimeout(function(){
$httpBackend.flush();
}, 50);
}, 50);
});
现在我很确定有些事情我做得不对,但我不能指责它:
- 我是不是用错了承诺?
- 我应该模拟 XMLParser 和 localStorage 吗?
- 我的茉莉花有没有错误?
- 我是否过度使用 $q?
感谢您的帮助!
好吧,我最终模拟了 XMLParser 和 localStorage。我得出的结论是,再次重新测试这些服务是没有意义的。这是一个单元测试,应该只测试一个单元。
我还按照 Explosion Pills 的建议修改了我对 promises 的使用。
//Load the categories
function loadCategories(){
var structure = { ... }
return localStorage.getStructure()
.then(function(struct){
return struct.category;
})
.catch(function(e){
if(e.name != 'not_found') return e;
return xmlparser.readFile(apiUrl, structure)
.then(function(doc){
localStorage.saveStructure(doc);
return doc.category;
});
});
}
我像以前一样添加了模拟
beforeEach(module('gisMobile', function($provide){
xmlParserMock = {
readFile: function(){
var defer = $q.defer();
defer.resolve(indicatorJsonMock);
return defer.promise;
}
};
localStorageMock = {
isStructureCached: false,
getStructure: function(){
var defer = $q.defer();
if(localStorageMock.isStructureCached)
defer.resolve(indicatorJsonMock);
else
defer.reject({name: 'not_found'});
return defer.promise;
},
saveStructure: function(){
localStorageMock.isStructureCached = true;
}
}
$provide.value('xmlparser', xmlParserMock);
$provide.value('localStorage', localStorageMock);
}));
最终测试现在看起来像
it('should list the categories', function(done){
var promise = Indicator.getCategories()
.then(function(categories){
expect(categories[1].name).toBe('Emplois par secteur d\'activité');
expect(categories[0].name).toBe('Emplois par niveau de compétence');
done();
})
.catch(function(e){
expect(e).toBe(null);
});
$rootScope.$digest();
});
所以为了解决这个问题,我的假设是正确的:
- 我没有正确使用承诺
- 我不得不模拟 xmlParser 和 localStorage,因为它们不是本次测试的目标。