Angularjs 监视服务依赖关系
我很好奇监视依赖项的最佳方法,这样我就可以确保在我的服务中调用它们的方法。我简化了代码,将重点放在手头的问题上。我能够很好地测试我的服务,但我还希望能够确认我的服务(在本例中是metricService)具有也被调用的方法。我知道我必须以某种方式使用createSpyObj,但是当函数正确执行时,spyObj方法不会被捕获。我应该使用createSpyObj吗?还是我应该用spyObj?当涉及到依赖性时,我对间谍的概念感到困惑 更新:当使用SpyOn时,我可以看到一个方法被调用,但其他方法没有 测试规范Angularjs 监视服务依赖关系,angularjs,unit-testing,karma-jasmine,Angularjs,Unit Testing,Karma Jasmine,我很好奇监视依赖项的最佳方法,这样我就可以确保在我的服务中调用它们的方法。我简化了代码,将重点放在手头的问题上。我能够很好地测试我的服务,但我还希望能够确认我的服务(在本例中是metricService)具有也被调用的方法。我知道我必须以某种方式使用createSpyObj,但是当函数正确执行时,spyObj方法不会被捕获。我应该使用createSpyObj吗?还是我应该用spyObj?当涉及到依赖性时,我对间谍的概念感到困惑 更新:当使用SpyOn时,我可以看到一个方法被调用,但其他方法没有
describe("Catalogs service", function() {
beforeEach(angular.mock.module("photonServicesCommons"));
var utilityService, metricsService, loggerService, catalogService, localStorageService;
var $httpBackend, $q, $scope;
beforeEach(
inject(function(
_catalogService_,
_metricsService_,
_$rootScope_,
_$httpBackend_
) {
catalogService = _catalogService_;
$scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', "/ctrl/catalog/all-apps").respond(
{
catalogs: catalogs2
}
);
metricsService = _metricsService_;
startScope = spyOn(metricsService, 'startScope')
emitSuccess = spyOn(metricsService, 'emitGetCatalogSuccess').and.callThrough();
endScope = spyOn(metricsService, 'endScope');
})
);
afterEach(function(){
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('get catalog', function(){
it("Should get catalogs", function(done) {
catalogService.normalizedDynamicAppList = testDynamicAppList1;
catalogService.response = null;
var promise3 = catalogService.getCatalog();
promise3.then(function (res) {
expect(res.catalogs).toEqual(catalogs2);
});
expect(metricsService.startScope).toHaveBeenCalled();
expect(metricsService.emitGetCatalogSuccess).toHaveBeenCalled();
expect(metricsService.endScope).toHaveBeenCalled();
$scope.$digest();
done();
$httpBackend.flush();
});
});
});
服务
public getCatalog(): IPromise<Interfaces.CatalogsResponse> {
if (this.response !== null) {
let finalResponse:any = angular.copy(this.response);
return this.$q.when(finalResponse);
}
return this.$q((resolve, reject) => {
this.metricsService.startScope(Constants.Photon.METRICS_GET_CATALOG_TIME);
this.$http.get(this.catalogEndpoint).then( (response) => {
let data: Interfaces.CatalogsResponse = response.data;
let catalogs = data.catalogs;
if (typeof(catalogs)) { // truthy check
catalogs.forEach((catalog: ICatalog) => {
catalog.applications.forEach((application: IPhotonApplication) => {
if( !application.appId ) {
application.appId = this.utilityService.generateUUID();
}
})
});
} else {
this.loggerService.error(this.TAG, "Got an empty catalog.");
}
this.response = data;
this.metricsService.emitGetCatalogSuccess();
console.log("CALLING END SCOPE");
this.metricsService.endScope(Constants.Photon.METRICS_GET_CATALOG_TIME);
resolve(finalResponse);
}).catch((data) => {
this.loggerService.error(this.TAG, "Error getting apps: " + data);
this.response = null;
this.metricsService.emitGetCatalogFailure();
reject(data);
});
});
} // end of getCatalog()
public getCatalog():IPromise{
if(this.response!==null){
让finalResponse:any=angular.copy(this.response);
返回此项。$q.when(最终响应);
}
返回此项。$q((解决、拒绝)=>{
this.metricsService.startScope(Constants.Photon.METRICS\u GET\u CATALOG\u TIME);
this.$http.get(this.catalogEndpoint)。然后((响应)=>{
let data:Interfaces.CatalogsResponse=response.data;
设catalogs=data.catalogs;
如果(目录类型)){//truthy检查
目录.forEach((目录:ICatalog)=>{
catalog.applications.forEach((应用程序:IPhotonApplication)=>{
如果(!application.appId){
application.appId=this.utilityService.GenerateUid();
}
})
});
}否则{
this.loggerService.error(this.TAG,“获取空目录”);
}
这个响应=数据;
此.metricsService.emitGetCatalogSuccess()文件;
log(“调用端作用域”);
this.metricsService.endScope(Constants.Photon.METRICS\u GET\u CATALOG\u TIME);
解决(最终责任);
}).catch((数据)=>{
this.loggerService.error(this.TAG,“获取应用程序时出错:+数据”);
this.response=null;
this.metricsService.emitGetCatalogFailure();
拒绝(数据);
});
});
}//getCatalog()的结尾
不用createSpyObj,只需使用spyOn即可。例如:
beforeEach(
inject(function(
_catalogService_,
_$rootScope_,
_$httpBackend_,
_metricsService_ //get the dependecy from the injector & then spy on it's properties
) {
catalogService = _catalogService_;
metricsService = _metricsService_;
$scope = _$rootScope_.$new();
...
// create the spy object for easy referral later on
someMethodSpy = jasmine.spyOn(metricsService, "someMethodIWannaSpyOn")
})
);
describe('get catalog', function(){
it("Should get catalogs", function(done) {
catalogService.normalizedDynamicAppList = testDynamicAppList1;
catalogService.response = null;
var promise3 = catalogService.getCatalog();
...other expects
...
//do the spy-related expectations on the function spy object
$httpBackend.flush(); // this causes the $http.get() to "move forward"
// and execution moves into the .then callback of the request.
expect(someMethodSpy).toHaveBeenCalled();
});
});
我在测试复杂的angular应用程序时使用了这种模式,并将外部导入/全局依赖项包装在angular服务包装器中,以允许监视和模拟它们进行测试
createSpyObject在这里不起作用的原因是,使用它将创建一个名为metricService的全新对象,并指定间谍道具。它将不同于被角度注入器注入正在测试的服务中的“metricService”。您希望从注入器获取实际相同的单例服务对象,然后监视其属性
功能障碍的另一个来源是$httpBackend.flush()
s位置。$httpBackend
是对$http服务的一个模拟:您预先定义了您正在测试的代码要发出的任何数量的预期http请求。然后,当您调用内部使用$http向某个url发出请求的函数时,$httpBackend会截取对$http方法的调用(并可以执行验证请求负载和头以及响应等操作)。
只有在测试代码调用$httpBackend.flush()
之后,才会调用$http
调用的then/error处理程序。这允许您执行准备某些测试状态所需的任何类型的设置,然后触发。然后
处理程序并继续执行异步逻辑
就我个人而言,每次我用$httpBackend编写测试时都会发生同样的事情,而且总是需要一段时间才能弄清楚或记住:)谢谢你的回复。这在一定程度上是可行的。如果你看我编辑的帖子,当我监视“startScope”时,我可以看到它按预期被调用,但是当我监视“endScope”或“emitGetCatalogSuccess”时,我的测试没有检测到它们被调用,即使我在日志中看到这些方法实际上被调用了。有什么想法吗?上面的代码片段似乎是没有spyOn的旧代码片段,你能不能也显示一个有spyOn的代码片段?啊,还有,你需要先刷新httpBackend,然后才能期待间谍被调用!这可能就足够了,有时您还需要调用$timeout.flush()来强制所有$q异步内容也刷新。我更新了代码,但也将重新排序我的刷新调用thanksGlad。为了帮助您,我在答案中添加了一些关于刷新的内容。异步内容的测试可能是一个真正的痛苦!