Angular 4.2.4组件getMotivos方法subscribe逻辑单元测试问题
Angular 4.2.4单元测试:验证subscribe回调后showLoading状态变更
我们使用Angular 4.2.4,需要为组件编写单元测试,目标是测试MotivosCancelamentoComponent中getMotivos方法内的subscribe回调,验证调用该方法后showLoading变量是否变为false,且需要模拟服务返回值。之前尝试多个方案均未成功。
待测试组件代码(motivos-cancelamento.component.ts)
export class MotivosCancelamentoComponent implements OnInit { showLoading = true; constructor( private service: MotivosCancelamentoService, ) { } ngOnInit() { this.getMotivos(); } getMotivos() { this.service.getMotivos().subscribe((data) => { this.showLoading = false; }, (error: any) => { this.redirectError(); }); } }
当前编写的单元测试代码(motivos-cancelamento.component.spec.ts)
describe('MotivosCancelamentoComponent', () => { let component: MotivosCancelamentoComponent; let service: MotivosCancelamentoService; let fixture: ComponentFixture<MotivosCancelamentoComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ MotivosCancelamentoComponent ], providers: [ { provide: MotivosCancelamentoService, useValue: motivosCancelamentoServiceStub } ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(MotivosCancelamentoComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it("should call getMotivos and return showLoading value as false", async(() => { const showLoading = false; spyOn(service, 'getMotivos').and.returnValue(showLoading) fixture.detectChanges(); expect(component.showLoading).toEqual(showLoading); })); });
相关服务代码(motivos-cancelamento.service.ts)
import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; export interface Entries { entries: any[]; } const motivos = { 'entries': [{ '_version': 1, } ] } @Injectable() export class MotivosCancelamentoService { constructor() { } getMotivos(): Observable<object> { return of(motivos); } } export const motivosCancelamentoServiceStub = { getMotivos: ()=> { return of(motivos) } }
测试失败原因分析
- 服务实例未正确获取:测试中声明了
service变量,但未通过TestBed.get获取实例,导致spyOn调用时service为undefined。 - 返回值类型错误:
getMotivos方法要求返回Observable,但测试中直接返回布尔值,导致subscribe无法执行回调。 - 生命周期触发时机错误:
fixture.detectChanges()在组件初始化时就触发了ngOnInit,此时spy还未设置,实际调用的是原stub方法,测试逻辑未生效。
修正后的单元测试代码
describe('MotivosCancelamentoComponent', () => { let component: MotivosCancelamentoComponent; let service: MotivosCancelamentoService; let fixture: ComponentFixture<MotivosCancelamentoComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [MotivosCancelamentoComponent], providers: [ { provide: MotivosCancelamentoService, useValue: motivosCancelamentoServiceStub } ] }).compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(MotivosCancelamentoComponent); component = fixture.componentInstance; // 先获取服务实例,暂不触发ngOnInit service = TestBed.get(MotivosCancelamentoService); }); it('should set showLoading to false after getMotivos succeeds', (done) => { // 验证初始状态为true expect(component.showLoading).toBe(true); // 创建模拟Observable并spy服务方法 const mockData = { entries: [] }; const mock$ = of(mockData); spyOn(service, 'getMotivos').and.returnValue(mock$); // 调用组件方法 component.getMotivos(); // 订阅完成后验证状态变更 mock$.subscribe(() => { expect(component.showLoading).toBe(false); done(); // 标记异步测试完成 }); }); it('should update showLoading after ngOnInit triggers getMotivos', () => { // spy服务方法并保留原stub逻辑 spyOn(service, 'getMotivos').and.callThrough(); // 触发ngOnInit fixture.detectChanges(); // 验证服务方法被调用,且状态变更 expect(service.getMotivos).toHaveBeenCalled(); expect(component.showLoading).toBe(false); }); });
关键修正说明
- 获取服务实例:在
beforeEach中通过TestBed.get(MotivosCancelamentoService)获取服务实例,确保spy能正确挂载。 - 控制生命周期触发:延迟调用
fixture.detectChanges(),先设置spy再触发ngOnInit或调用组件方法。 - 返回正确类型:使用
of()创建模拟Observable,保证subscribe回调能正常执行。 - 异步测试处理:针对subscribe的异步特性,使用
done()回调通知测试框架异步操作完成,避免测试提前结束。
内容的提问来源于stack exchange,提问作者Oo'-
相关产品推荐
相关产品推荐

