Angular+JHipster测试中jhiTranslate致Element引用未定义的解决方法
问题还原
你遇到的这个场景太典型了:当测试带有jhiTranslate指令的label元素时,@ViewChild死活拿不到元素引用,报“hidden未定义”的错误;但一旦移除指令,测试立刻正常。再加上你的TestBed还没引入任何JHipster翻译相关依赖,这就导致指令无法正常初始化,进一步打乱了元素的渲染时机,让引用绑定彻底失败。
再明确下你的核心场景:
带指令的label代码:
<label #hiddenLabel class="form-control-label" jhiTranslate="oncosupApp.paciente.nombre" for="field_nombre" >Nombre</label>
组件内的@ViewChild定义:
@ViewChild('hiddenLabel') hidden: ElementRef;
测试代码中直接访问comp.hidden.nativeElement就触发了错误。
原因分析
jhiTranslate是JHipster封装的翻译指令,它的工作逻辑是异步加载翻译文本并替换DOM内容,甚至可能修改DOM结构。而你的测试环境缺少翻译服务依赖,指令无法正常运行,导致Angular在组件初始化阶段,@ViewChild找不到对应的元素——指令的异常处理或异步过程完全打乱了元素的渲染节奏。
解决方案
这里给你几个可行的方案,按推荐程度排序:
1. 引入Mock翻译服务,让指令正常工作
既然不能移除指令,那就在测试环境里给它提供必要的依赖,让它能正常运行,这样DOM渲染就不会出问题。
首先写一个简单的Mock翻译服务,再修改TestBed配置:
import { JhiTranslateModule, JhiTranslateService } from 'ng-jhipster'; // 自定义Mock翻译服务,测试时直接返回需要的文本 class MockJhiTranslateService extends JhiTranslateService { instant(key: string): string { // 针对测试场景返回指定文本,其他情况返回key本身 return key.includes('nombre') ? 'Nombre' : key; } // 翻译服务的核心方法需要实现,这里简单返回Promise get(key: string): Promise<string> { return Promise.resolve(this.instant(key)); } } // 修改TestBed配置,引入翻译模块并替换为Mock服务 beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ OncosupTestModule, OncosupSharedModule, BrowserModule, FormsModule, JhiTranslateModule.forRoot() // 添加翻译模块 ], declarations: [PacienteDialogComponent], providers: [ JhiAlertService, // 替换成Mock翻译服务 { provide: JhiTranslateService, useClass: MockJhiTranslateService } ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents(); }));
然后调整测试代码,等待异步操作完成:
fit('first test asdsadfdfd', async(() => { fixture.detectChanges(); // 触发初始渲染 await fixture.whenStable(); // 等待翻译指令的异步操作完成 expect(comp.hidden.nativeElement.innerHTML).toContain('Nombre'); }));
2. 调整@ViewChild的静态配置
Angular 8+之后,@ViewChild默认是static: false,这个配置会让Angular在变更检测之后再查找元素,适配异步渲染的场景。如果你的组件里没显式配置,也可以加上确认下:
@ViewChild('hiddenLabel', { static: false }) hidden: ElementRef;
这个方法可以配合上面的方案一起用,进一步确保元素引用能被正确获取。
3. 临时方案:Override指令,让它变成空指令
如果不想引入太多翻译相关依赖,也可以直接在测试里把jhiTranslate指令替换成一个不做任何操作的空指令,这样它就不会干扰DOM渲染了:
import { JhiTranslateDirective } from 'ng-jhipster'; // 在TestBed配置里添加overrideDirective beforeEach(async(() => { TestBed.configureTestingModule({ // ... 你的原有配置 }) .overrideDirective(JhiTranslateDirective, { set: { // 直接渲染原始内容,不做翻译处理 template: '{{content}}', selector: '[jhiTranslate]' } }) .compileComponents(); }));
这样指令就不会修改DOM,@ViewChild就能正常拿到元素引用了,测试代码不需要额外修改。
内容的提问来源于stack exchange,提问作者Tester

