Angular组件Jasmine单元测试报input.setFocus is not a function如何解决
问题根因
@ViewChild查询返回的是模板内ion-input对应的IonInput组件实例,不属于模块全局注入的依赖,你在TestBed providers里全局配置的ElementRef、IonInputmock无法覆盖模板生成的组件实例,配置完全不生效。ngAfterViewInit内使用了800ms的setTimeout异步逻辑,单元测试默认不会主动等待定时器执行,若不手动处理异步任务,定时器回调执行时拿到的实例状态异常。- 你定义的Mock类属性未显式初始化,运行时
input['el']实际取值为undefined,调用setFocus自然抛出未定义错误。 - 补充:你代码里
_setFocusDefaultInput方法参数声明为ElementRef类型,但实际传入的inputUsername是IonInput组件实例,类型不匹配也导致你按ElementRef做的mock完全不对位。
解决方案
1. 直接给@ViewChild属性赋值Mock
不需要在TestBed全局配置provider,直接在测试用例中给组件的inputUsername属性赋值符合预期结构的mock即可:
// 在测试的beforeEach中,组件实例创建后、变更检测前添加 fixture = TestBed.createComponent(LoginPage); component = fixture.componentInstance; // 手动构造符合调用逻辑的mock component.inputUsername = { el: { setFocus: jasmine.createSpy('setFocus') } } as any; fixture.detectChanges();
2. 处理setTimeout异步逻辑
使用Angular测试工具的fakeAsync和tick模拟时间流逝,触发定时器回调执行:
import { fakeAsync, tick } from '@angular/core/testing'; it('should trigger focus on username input after view init', fakeAsync(() => { // 触发ngAfterViewInit生命周期 fixture.detectChanges(); // 模拟800ms时间流逝,触发setTimeout回调 tick(800); // 验证setFocus方法被正确调用 expect(component.inputUsername.el.setFocus).toHaveBeenCalled(); }));
3. 可选优化(类型规范)
可以修正组件代码的类型声明,避免运行时和声明类型不一致的问题:
// 声明@ViewChild的类型为IonInput @ViewChild('input_username') inputUsername: IonInput; private _setFocusDefaultInput(input: IonInput): void { input.el.setFocus(); }
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

