如何在ngAfterContentInit钩子前Mock inputControl?组件实现是否增加测试难度?
问题
我有一个组件,通过以下代码查询内容子元素:
@ContentChild(InputControlDirective, { read: ElementRef }) public inputControl: ElementRef;
我在ngAfterContentInit钩子中检查inputControl是否存在,不存在就抛出自定义错误:
public ngAfterContentInit(): void { if (!this.inputControl) { throw new Error( 'Provide a InputControlDirective for the field used with the form-input component like <input inputControl>' ); } }
针对下面的单元测试代码,怎么在ngAfterContentInit钩子调用前Mock inputControl?
describe('FormInputComponent', () => { let component: FormInputComponent; let fixture: ComponentFixture<FormInputComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [FormInputComponent], }).compileComponents(); fixture = TestBed.createComponent(FormInputComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
另外,是不是组件的实现方式导致单元测试难写?
解答
一、Mock inputControl的两种方法
方法1:直接给组件实例赋值
fixture.detectChanges()会触发组件的初始化流程(包括ngAfterContentInit),所以你可以在调用这个方法之前,直接给component.inputControl塞一个Mock的ElementRef,这样钩子执行时就不会报错:
beforeEach(async () => { await TestBed.configureTestingModule({ imports: [FormInputComponent], }).compileComponents(); fixture = TestBed.createComponent(FormInputComponent); component = fixture.componentInstance; // 提前Mock inputControl component.inputControl = { nativeElement: document.createElement('input') } as ElementRef; fixture.detectChanges(); // 此时才触发初始化钩子 });
方法2:用宿主组件模拟真实使用场景
如果想更贴近实际使用情况,可以创建一个测试用的宿主组件,在它的模板里把form-input和带inputControl指令的输入框组合起来,再测试这个宿主组件:
// 定义测试宿主组件 @Component({ template: ` <form-input> <input inputControl> </form-input> ` }) class TestHostComponent {} describe('FormInputComponent', () => { let component: FormInputComponent; let fixture: ComponentFixture<TestHostComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [TestHostComponent], imports: [FormInputComponent, InputControlDirective] }).compileComponents(); fixture = TestBed.createComponent(TestHostComponent); // 获取嵌套的FormInputComponent实例 component = fixture.debugElement.query(By.directive(FormInputComponent)).componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
二、组件实现是否有问题?
你的组件实现没问题——用ContentChild依赖内容子元素,再在初始化钩子做必填校验,这是确保组件被正确使用的常规操作。测试遇到的麻烦只是Angular测试框架的执行顺序问题:默认直接创建组件时没有提供内容子元素,fixture.detectChanges()触发初始化后自然会报错。
这种情况是Angular内容投影组件测试的常规场景,只要掌握上面的Mock或宿主组件测试方法,就能轻松解决,不是组件设计上的问题。
内容的提问来源于stack exchange,提问作者Thabo
相关产品推荐
相关产品推荐

