Angular表单验证单元测试问题:控件始终处于未触碰状态
Angular表单验证单元测试问题:控件始终为untouched,错误信息不显示
组件HTML代码
<form #zipForm="ngForm"> <label> <input type="text" id="postalCode" [(ngModel)]="postalCode" #postalCodeElem="ngModel" name="postalCode" [pattern]="validationPattern" required > <button type="button" (click)="click(postalCodeElem.value)">Enroll</button> <div *ngIf="postalCodeElem.invalid && postalCodeElem.touched"> <span id="required-postal-code" *ngIf="postalCodeElem?.errors?.required"> zip/postal code is required </span> <span id="invalidPostalCode" *ngIf="postalCodeElem?.errors?.pattern"> zip postal code is invalid </span> </div> </label> </form>
测试代码
it('should show error in case of invalid postal code', () => { const elem: HTMLElement = fixture.nativeElement; const inputElem: HTMLInputElement = elem.querySelector('input'); inputElem.value = 'L5L2'; inputElem.dispatchEvent(new Event('input', { bubbles: true })); fixture.detectChanges(); const invalidErrorElem: any = elem.querySelector('span'); console.log('elem', elem); console.log('invalidErrorElem', invalidErrorElem); expect(invalidErrorElem).not.toBeNull(); });
问题描述
修改<input>框的值后,控件状态始终保持untouched,错误信息无法显示,请问哪里操作有误?
问题分析与解决
核心问题
你的错误信息渲染条件是postalCodeElem.invalid && postalCodeElem.touched,但测试里只完成了“输入内容”这一步(触发input事件),却没有让控件进入touched状态——Angular中表单控件的touched状态是在控件失去焦点(用户点击输入框外)时才会被标记的,你的测试缺少了这个关键步骤。
修复方法
在测试中添加blur事件的触发,模拟用户输入后离开输入框的行为:
it('should show error in case of invalid postal code', () => { const elem: HTMLElement = fixture.nativeElement; const inputElem: HTMLInputElement = elem.querySelector('input'); // 设置输入值并触发input事件更新模型 inputElem.value = 'L5L2'; inputElem.dispatchEvent(new Event('input', { bubbles: true })); // 触发blur事件,标记控件为touched inputElem.dispatchEvent(new Event('blur', { bubbles: true })); fixture.detectChanges(); // 通过id精准选择无效格式的错误提示元素 const invalidErrorElem = elem.querySelector('#invalidPostalCode'); expect(invalidErrorElem).not.toBeNull(); });
补充优化点
- 原来的
elem.querySelector('span')可能会误选必填提示的span,通过id="invalidPostalCode"选择目标元素,测试结果更准确。 - 如果你不想模拟用户交互,也可以直接通过控件实例手动标记为touched:
不过模拟import { By } from '@angular/platform-browser'; import { NgModel } from '@angular/forms'; // ... it('should show error in case of invalid postal code', () => { const elem: HTMLElement = fixture.nativeElement; const inputElem: HTMLInputElement = elem.querySelector('input'); inputElem.value = 'L5L2'; inputElem.dispatchEvent(new Event('input', { bubbles: true })); // 获取NgModel实例并标记为touched const ngModel = fixture.debugElement.query(By.css('#postalCode')).injector.get(NgModel); ngModel.control.markAsTouched(); fixture.detectChanges(); const invalidErrorElem = elem.querySelector('#invalidPostalCode'); expect(invalidErrorElem).not.toBeNull(); });blur事件的方式更贴近真实用户操作,推荐优先使用。
内容的提问来源于stack exchange,提问作者Shikasta_Kashti
相关产品推荐
相关产品推荐

