TypeError: Cannot read property 'fetchData' of undefined jasmine-karma
TypeError: Cannot read property 'fetchData' of undefined jasmine-karma
这是我在 angular 7 中的第一个单元测试,我想在我的服务中测试我的方法,这个方法 return Observable:
fetchData(chronoInfo: ALChrono): Observable<any> {
// construct API parameters and URL
var URL: string = this.urlDecoratorService.urlAPIDecorate("AL", "GetAccessChrono");
var params = this.urlDecoratorService.generateParameters({
year: chronoInfo.year,//Année
month: chronoInfo.month,//Mois (peut être null)
sortBy: chronoInfo.sortBy,// Champ de tri
sortDirection: chronoInfo.sortDirection,//True pour inverser l'ordre de tri
pageNumber: chronoInfo.currentPage,//Page de résultats
pageSize: chronoInfo.pageSize//Nb enregistrements par page
});
return this.apiFetcher.fetchJson(URL, params);
//retourne les AL par année & mois
}
这个方法的测试是:
import { TestBed, async, inject } from '@angular/core/testing';
import { AnnonceChronoDetailService } from './annonce-chrono-detail.service';
import { HttpClientModule } from '@angular/common/http';
import { UrlDecoratorService } from "src/app/common/url-decorator.service";
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { APIFetcherService } from "src/app/services/common/api-fetcher.service";
import { Http, ConnectionBackend, RequestOptions, HttpModule } from "@angular/http";
import { ALChrono } from '../../common/IALChrono.interface';
import { Observable } from 'rxjs';
describe('AnnonceChronoDetailService', () => {
let service: AnnonceChronoDetailService;
let alChrono: ALChrono = { //it's an interface'
year: 2012,
month: -1,
sortBy: 'asc',
sortDirection: true,
currentPage: 1,
pageSize: 15
}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
AnnonceChronoDetailService,
UrlDecoratorService,
APIFetcherService,
Http,
ConnectionBackend]
});
});
it('should be created', inject([AnnonceChronoDetailService], (service: AnnonceChronoDetailService) => {
expect(service).toBeTruthy();
}
));
it('#fetchData should return value from observable', (done: DoneFn) => {
service.fetchData(alChrono).subscribe(value => {
expect(value).toBe('observable value');
done();
});
});
});
当我执行 ng test
时,我在第二个测试 #fetchData should return value from observable
中遇到问题,错误是:
AnnonceChronoDetailService > #fetchData should return value from observable
TypeError: Cannot read property 'fetchData' of undefined
at <Jasmine>
at UserContext.<anonymous> (http://localhost:9876/src/app/services/annonce-legale/annonce-chrono-detail.service.spec.ts?:41:17)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/node_modules/zone.js/dist/zone.js?:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/node_modules/zone.js/dist/zone.js?:387:1)
at Zone../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9876/node_modules/zone.js/dist/zone.js?:138:1)
at runInTestZone (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:506:1)
at UserContext.<anonymous> (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:522:1)
at <Jasmine>
我尝试了一些解决方案,例如添加 asyn
,但问题是一样的。
您在第一个规范('it' 函数)中定义了 'service',但随后尝试在第二个规范中使用它。如果您想在每个规范中使用 'service' 变量,这是有道理的,那么我建议您将类似的东西放在 beforeEach 函数中,以便为每个规范正确定义服务:
service = TestBed.get(AnnonceChronoDetailService);
然后将您的第一个规格更改为以下内容:
it('should be created', () => {
expect(service).toBeTruthy();
});
现在您的第二个规范应该可以正常工作了。
更新:
为您的其他服务添加间谍。有很多方法可以做到这一点。这是一个:
describe('AnnonceChronoDetailService', () => {
const spyDecorator = jasmine.createSpyObj('UrlDecoratorService', {
urlAPIDecorate: '/test/url',
generateParameters: null /* set test params to return here /*
};
const spyFetcher = jasmine.createSpyObj('APIFetcherService', {
fetchJson: {}
};
let service: AnnonceChronoDetailService;
...
现在更改您的提供商数组以使用间谍而不是原始服务:
providers: [
AnnonceChronoDetailService,
{ provide: UrlDecoratorService, useValue: spyDecorator } ,
{ provide: APIFetcherService, useValue: spyFetcher },
]
然后在您的规范文件中,您可以检查那些间谍。将这些建议更改为您真正关心的内容:
it('#fetchData should return value from observable', () => {
spyFetcher.fetchJson.and.returnValue(of('observable value'));
service.fetchData(alChrono).subscribe(value => {
expect(value).toEqual('observable value');
});
expect(spyDecorator.urlAPIDecorate).toHaveBeenCalled();
expect(spyDecorator.generateParameters).toHaveBeenCalledTimes(1);
expect(spyFetcher.fetchJson).toHaveBeenCalledWith('/test/url', /* test params defined above */);
});
这是我在 angular 7 中的第一个单元测试,我想在我的服务中测试我的方法,这个方法 return Observable:
fetchData(chronoInfo: ALChrono): Observable<any> {
// construct API parameters and URL
var URL: string = this.urlDecoratorService.urlAPIDecorate("AL", "GetAccessChrono");
var params = this.urlDecoratorService.generateParameters({
year: chronoInfo.year,//Année
month: chronoInfo.month,//Mois (peut être null)
sortBy: chronoInfo.sortBy,// Champ de tri
sortDirection: chronoInfo.sortDirection,//True pour inverser l'ordre de tri
pageNumber: chronoInfo.currentPage,//Page de résultats
pageSize: chronoInfo.pageSize//Nb enregistrements par page
});
return this.apiFetcher.fetchJson(URL, params);
//retourne les AL par année & mois
}
这个方法的测试是:
import { TestBed, async, inject } from '@angular/core/testing';
import { AnnonceChronoDetailService } from './annonce-chrono-detail.service';
import { HttpClientModule } from '@angular/common/http';
import { UrlDecoratorService } from "src/app/common/url-decorator.service";
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { APIFetcherService } from "src/app/services/common/api-fetcher.service";
import { Http, ConnectionBackend, RequestOptions, HttpModule } from "@angular/http";
import { ALChrono } from '../../common/IALChrono.interface';
import { Observable } from 'rxjs';
describe('AnnonceChronoDetailService', () => {
let service: AnnonceChronoDetailService;
let alChrono: ALChrono = { //it's an interface'
year: 2012,
month: -1,
sortBy: 'asc',
sortDirection: true,
currentPage: 1,
pageSize: 15
}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
AnnonceChronoDetailService,
UrlDecoratorService,
APIFetcherService,
Http,
ConnectionBackend]
});
});
it('should be created', inject([AnnonceChronoDetailService], (service: AnnonceChronoDetailService) => {
expect(service).toBeTruthy();
}
));
it('#fetchData should return value from observable', (done: DoneFn) => {
service.fetchData(alChrono).subscribe(value => {
expect(value).toBe('observable value');
done();
});
});
});
当我执行 ng test
时,我在第二个测试 #fetchData should return value from observable
中遇到问题,错误是:
AnnonceChronoDetailService > #fetchData should return value from observable
TypeError: Cannot read property 'fetchData' of undefined
at <Jasmine>
at UserContext.<anonymous> (http://localhost:9876/src/app/services/annonce-legale/annonce-chrono-detail.service.spec.ts?:41:17)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/node_modules/zone.js/dist/zone.js?:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/node_modules/zone.js/dist/zone.js?:387:1)
at Zone../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9876/node_modules/zone.js/dist/zone.js?:138:1)
at runInTestZone (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:506:1)
at UserContext.<anonymous> (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:522:1)
at <Jasmine>
我尝试了一些解决方案,例如添加 asyn
,但问题是一样的。
您在第一个规范('it' 函数)中定义了 'service',但随后尝试在第二个规范中使用它。如果您想在每个规范中使用 'service' 变量,这是有道理的,那么我建议您将类似的东西放在 beforeEach 函数中,以便为每个规范正确定义服务:
service = TestBed.get(AnnonceChronoDetailService);
然后将您的第一个规格更改为以下内容:
it('should be created', () => {
expect(service).toBeTruthy();
});
现在您的第二个规范应该可以正常工作了。
更新:
为您的其他服务添加间谍。有很多方法可以做到这一点。这是一个:
describe('AnnonceChronoDetailService', () => {
const spyDecorator = jasmine.createSpyObj('UrlDecoratorService', {
urlAPIDecorate: '/test/url',
generateParameters: null /* set test params to return here /*
};
const spyFetcher = jasmine.createSpyObj('APIFetcherService', {
fetchJson: {}
};
let service: AnnonceChronoDetailService;
...
现在更改您的提供商数组以使用间谍而不是原始服务:
providers: [
AnnonceChronoDetailService,
{ provide: UrlDecoratorService, useValue: spyDecorator } ,
{ provide: APIFetcherService, useValue: spyFetcher },
]
然后在您的规范文件中,您可以检查那些间谍。将这些建议更改为您真正关心的内容:
it('#fetchData should return value from observable', () => {
spyFetcher.fetchJson.and.returnValue(of('observable value'));
service.fetchData(alChrono).subscribe(value => {
expect(value).toEqual('observable value');
});
expect(spyDecorator.urlAPIDecorate).toHaveBeenCalled();
expect(spyDecorator.generateParameters).toHaveBeenCalledTimes(1);
expect(spyFetcher.fetchJson).toHaveBeenCalledWith('/test/url', /* test params defined above */);
});