Angular测试问题:无法初始化继承基础组件的子组件
问题描述
我正在测试一个继承自BaseComponent的SubComponent,该子组件重写了父组件的ngOnInit()方法,而基础组件的ngOnInit和ngOnDestroy方法中包含相关逻辑。测试时出现以下错误:
component threw errors during cleanup
请问如何正确地隔离并初始化该子组件进行测试?
SubComponent 代码
@Component({ selector: '', templateUrl: '', }) export class SubComponent extends BaseComponent<any> implements OnInit, AfterViewInit { constructor( protected override service1: Service1<any>, protected override service2: Service2, ) { super(service1, service2, true); } override ngOnInit(): void { super.ngOnInit(); } }
BaseComponent 代码
@Component({ selector: 'base', standalone: true, template: '', }) export class BaseDialogComponent<T> implements OnInit, OnDestroy { constructor( protected service1: Service1<T>, protected service2: Service2, @Inject(DIALOG_CLOSING_CONFIG) dialogClosingConfig: boolean ) { this.enableDialogClosing = dialogClosingConfig; } @HostListener('window:popstate', ['$event']) onPopState(event: any) { ..... } ngOnInit() { .... } ngOnDestroy() { this.service2.something(); } }
SubComponent.spec 代码
describe('SubComponent', () => { let component: SubComponent; let fixture: ComponentFixture<SubComponent>; let someSubComponentService: SubComponentService; let subComponentServiceStub: Partial<any>; const createComponent = () => { fixture = TestBed.createComponent(SubComponent); fixture.detectChanges(); return fixture; }; beforeEach(async () => { subComponentServiceStub= { initialize: () => {}, }; await TestBed.configureTestingModule({ imports: [TestModule], declarations: [SubComponent], providers: [ { provide: SubComponentService, useValue: subComponentServiceStub, }, ], }).compileComponents(); component = createComponent().componentInstance; someSubComponentService= TestBed.inject( SubComponentService ); spyOn(someSubComponentService, 'initialize'); }); it('should create', () => { expect(component).toBeTruthy(); }); });
解决方案
这个错误通常是组件销毁阶段(ngOnDestroy)抛出异常导致的,结合你的代码来看,核心问题是未正确模拟父组件依赖和未妥善清理测试资源,具体修复步骤如下:
1. 补全父组件所需的依赖注入
父组件BaseDialogComponent需要DIALOG_CLOSING_CONFIG、Service1、Service2三个依赖,你的测试模块仅提供了SubComponentService,缺少这些关键依赖会导致初始化或销毁时出错:
await TestBed.configureTestingModule({ imports: [TestModule], declarations: [SubComponent], providers: [ { provide: SubComponentService, useValue: subComponentServiceStub }, // 模拟父组件必备依赖 { provide: DIALOG_CLOSING_CONFIG, useValue: true }, { provide: Service1, useValue: {} as Service1<any> }, // 给Service2的something方法创建Spy,避免销毁时抛出异常 { provide: Service2, useValue: { something: jasmine.createSpy('something') } } ], }).compileComponents();
2. 测试后手动清理组件与全局事件
测试结束后需主动触发组件销毁,并清理父组件绑定的全局事件监听(比如window:popstate):
afterEach(() => { // 触发组件销毁生命周期 fixture.destroy(); // 移除window上的popstate监听 window.removeEventListener('popstate', component.onPopState); });
3. 按需控制组件初始化时机
避免在beforeEach中直接调用fixture.detectChanges()(会自动触发ngOnInit),将初始化逻辑交给测试用例按需触发:
// 修改createComponent方法,不自动执行变更检测 const createComponent = () => { fixture = TestBed.createComponent(SubComponent); return fixture; }; // 在测试用例中手动触发初始化 it('should create', () => { component = createComponent().componentInstance; fixture.detectChanges(); // 手动触发ngOnInit expect(component).toBeTruthy(); });
4. 验证父组件方法调用(可选)
如果需要验证子组件是否正确调用父组件的ngOnInit,可以给父组件原型方法创建Spy:
beforeEach(() => { // Spy父组件的ngOnInit方法 spyOn(BaseDialogComponent.prototype, 'ngOnInit'); }); it('should call parent ngOnInit', () => { component = createComponent().componentInstance; fixture.detectChanges(); expect(BaseDialogComponent.prototype.ngOnInit).toHaveBeenCalled(); });
内容的提问来源于stack exchange,提问作者tsiro
相关产品推荐
相关产品推荐

