【发布时间】:2017-11-02 16:21:09
【问题描述】:
我正在尝试对使用 Observable.zip() 的组件进行单元测试
服务代码
如您所见,BehaviorSubject 是用 null 初始化的,但在测试代码的 beforeAll 中,我强制他返回一个 >Observable.of
export class ProfiloUtenteService extends BaseService<ProfiloDto> {
public static readonly profiloKey = 'profiloUtente';
private _$profilo = new BehaviorSubject<ProfiloDto>(null);
public $profilo = this._$profilo.asObservable();
protected get storedProfilo(): ProfiloDto {
const profilo = this.storageService.retrieve(ProfiloUtenteService.profiloKey);
return profilo ? profilo : null;
}
protected set storedProfilo(profilo: ProfiloDto) {
this.storageService.store(ProfiloUtenteService.profiloKey, profilo);
this._$profilo.next(profilo);
}
private emptyProfile = {
addettoId: null,
scope: Scope.none,
selectedScope: Scope.none,
ufficioId: null,
provinciaUfficioId: null,
addettoCognome: null,
addettoNome: null
};
constructor(
protected httpClient: HttpClient,
protected appConfig: AppConfig,
protected storageService: StorageService
) {
super(httpClient, appConfig, appConfig.endpoints.addetto.api.baseUrl);
const profilo = this.storedProfilo;
if (profilo) {
this._$profilo.next(profilo);
}
}
public updateProfiloUtente(): void {
const readProfilo = this.storedProfilo;
if (readProfilo) {
this._$profilo.next(readProfilo);
}else {
super.get((<AppConfig>this.appConfig).endpoints.addetto.api.routes.profilo)
.takeLast(1)
.do(profilo => {
if (!profilo) {
this._$profilo.next(this.emptyProfile);
}
})
.filter(profilo => !!profilo)
.subscribe(profilo => {
profilo.selectedScope = Scope.all;
this.storedProfilo = profilo;
});
}
}
public setSelectedScope(scope: Scope) {
const profilo = this.storedProfilo;
if (profilo) {
profilo.selectedScope = scope;
this.storedProfilo = profilo;
}
}
public setSelectedUfficioId(ufficioId: number, provinciaUfficioId?: number) {
const profilo = this.storedProfilo;
if (profilo) {
profilo.ufficioId = ufficioId;
profilo.provinciaUfficioId = provinciaUfficioId || null;
this.storedProfilo = profilo;
}
}
public logout() {
this.storedProfilo = null;
}
}
组件代码
这里爆炸了。当它订阅 Observable.zip 并尝试从中获取结果 [1] 时,此结果为 NULL。
Observable.zip(
this.ufficioService.getODataForCombo({ skip: 0 }),
this.profiloUtenteService.$profilo)
.takeWhile(() => this.isAlive)
.subscribe(result => {
result[0].forEach(office => this.availableOffices.push(office));
// when this point is reached an error is thrown
this.selectedOfficeId = result[1].ufficioId;
this.selectedOfficeDescription = this.availableOffices.find(office => office.id === this.selectedOfficeId).descrizione;
});
无法读取 null 的属性“ufficioId”
看起来压缩后的 observable 的结果 [1] 没有返回值。我也尝试用 Observable.combineLatest 切换 Observable.zip,但没有任何结果。错误是一样的。
测试代码:
beforeAll(() => {
ufficioServiceMock = new UfficioService(null, fixedAppConfig);
spyOn(ufficioServiceMock, 'getODataForCombo').and.returnValue(Observable.of([]));
profiloUtenteServiceMock = new ProfiloUtenteService(null, fixedAppConfig, null);
profiloUtenteServiceMock.$profilo = Observable.of({
addettoId: 1,
ufficioId: 1,
provinciaUfficioId: 1,
scope: 1,
addettoNome: 'string',
addettoCognome: 'string',
selectedScope: 1
});
});
新的测试平台代码版本(将服务 $profilo 更改为 getter)
即使将属性 $profilo 转换为 getter 并监视它返回一个值,也没有任何变化......
describe('PraticheSearchComponent', () => {
let comp: PraticheSearchComponent;
let fixture: ComponentFixture<PraticheSearchComponent>;
let de: DebugElement;
let el: HTMLElement;
let ufficioServiceMock: UfficioService;
let profiloUtenteServiceMock: ProfiloUtenteService;
beforeAll(() => {
ufficioServiceMock = new UfficioService(null, fixedAppConfig);
spyOn(ufficioServiceMock, 'getODataForCombo').and.returnValue(Observable.of([]));
profiloUtenteServiceMock = new ProfiloUtenteService(null, fixedAppConfig, null);
spyOn(profiloUtenteServiceMock, '$profilo').and.returnValue(new BehaviorSubject({
addettoId: 1,
ufficioId: 1,
provinciaUfficioId: 1,
scope: 1,
addettoNome: 'string',
addettoCognome: 'string',
selectedScope: 1
}).asObservable());
});
// profiloUtenteServiceMock.$profilo = new BehaviorSubject({
// addettoId: 1,
// ufficioId: 1,
// provinciaUfficioId: 1,
// scope: 1,
// addettoNome: 'string',
// addettoCognome: 'string',
// selectedScope: 1
// }).asObservable();
// });
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
PraticheSearchComponent
],
imports: [
NgProgressModule,
AuthModule.forRoot(),
CustomHttpHeadersModule.forRoot(),
SharedModule.forRoot(),
ReactiveFormsModule,
PatronatoSharedModule,
FrameworkCoreModule.forRoot(),
LoggerModule.forRoot(Level.LOG),
MaterialModule,
BrowserAnimationsModule,
RouterTestingModule.withRoutes([])
],
providers: [
{ provide: AppConfig, useValue: fixedAppConfig },
{ provide: LocalizationKeys, useValue: new LocalizationKeys() },
{ provide: ProfiloUtenteService, useValue: profiloUtenteServiceMock },
{ provide: NavbarService, useValue: new NavbarServiceMock() },
{ provide: PraticheSearchService, useValue: new PraticheSearchServiceMock() },
{ provide: UfficioService, useValue: ufficioServiceMock }
]
}).compileComponents();
fixture = TestBed.createComponent(PraticheSearchComponent);
comp = fixture.componentInstance;
de = fixture.debugElement;
el = de.nativeElement;
}));
it('should create', () => {
expect(comp).toBeTruthy();
});
});
我错过了什么吗?我从昨天早上开始就遇到了这个问题,我即将把我的工作站撞到地板上。任何帮助都非常感谢(机器:P)
【问题讨论】:
-
你能做一个jsbin的demo吗?你可以使用这个模板jsbin.com/vocozuy/edit?js,console
-
.takeWhile(() => this.isAlive)- 恭喜您在销毁时使用了更明智的退订方式! -
@martin 在 jsbin 上重现这个会有点复杂。这是一个 Angular 5 项目,包含 Jasmine、Typescript 和许多其他 npm 包。对于“简单”演示,我不知道从哪里开始使用 jsbin。我已经编辑了我的问题,因此您也可以看到原始服务。
标签: angular unit-testing jasmine rxjs