Angular Jasmine单元测试:实现mat-autocomplete输入值变更代码覆盖
Angular Jasmine单元测试:mat-autocomplete筛选功能的输入值变更测试
我正尝试为mat-autocomplete的筛选功能编写Angular Jasmine单元测试,通过输入数据过滤选项,但无法实现输入值变更的代码覆盖。
app.component.html
<div class="container"> <form [formGroup]="form"> <mat-form-field> <input matInput type="text" formControlName="name" [matAutocomplete]="auto1" /> <mat-autocomplete #auto1="matAutocomplete"> <!-- 修正原代码错误:绑定filteredNames而非原始data --> <mat-option *ngFor="let data of filteredNames | async" [value]="data.name"> {{ data.name }} </mat-option> </mat-autocomplete> </mat-form-field> </form> </div>
app.component.ts
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { map, Observable, startWith } from 'rxjs'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { data = [{ "id": 1, "name": "One" },{ "id": 2, "name": "Two" },{ "id": 3, "name": "Three" }]; // 声明筛选后的Observable类型 filteredNames!: Observable<any[]>; form: FormGroup = this.formBuilder.group({ name: [''], }); constructor(private formBuilder: FormBuilder) {} ngOnInit() { this.filteredNames = this.form.controls['name'].valueChanges.pipe( startWith(''), map((value) => this.getFilterData(value)) ); } getFilterData(value: string) { const filterValue = value.toLowerCase(); return this.data.filter((val) => val.name.toLowerCase().includes(filterValue)); } }
原测试代码(app.component.spec.ts)
import { HttpClientTestingModule } from '@angular/common/http/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { AppComponent } from './app.component'; const mockData = [{ "id": 1, "name": "One" },{ "id": 2, "name": "Two" },{ "id": 3, "name": "Three" }]; describe('AppComponent', () => { let component: AppComponent; let fixture: ComponentFixture<AppComponent>; const formBuilder: FormBuilder = new FormBuilder(); beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, FormsModule, ReactiveFormsModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ], declarations: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ { provide: FormBuilder, useValue: formBuilder }, ], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AppComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); it('getSysValuechanges.', () => { const spyFilter = spyOn<any>(component, 'getFilterData'); component.form.controls.name.setValue('Rambo'); expect(spyFilter).toHaveBeenCalled(); }); });
修正后的完整测试用例
核心测试逻辑说明
- 验证输入值变更时筛选函数被触发
- 验证筛选逻辑的正确性(不同输入对应不同筛选结果)
- 模拟用户真实输入行为,验证DOM层面的筛选触发
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { AppComponent } from './app.component'; const mockData = [{ "id": 1, "name": "One" },{ "id": 2, "name": "Two" },{ "id": 3, "name": "Three" }]; describe('AppComponent', () => { let component: AppComponent; let fixture: ComponentFixture<AppComponent>; const formBuilder: FormBuilder = new FormBuilder(); beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ FormsModule, ReactiveFormsModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ], declarations: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ { provide: FormBuilder, useValue: formBuilder }, ], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AppComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); // 测试输入值变更触发筛选函数 it('should call getFilterData when input value changes', () => { const spy = spyOn(component, 'getFilterData').and.callThrough(); component.form.get('name')?.setValue('o'); expect(spy).toHaveBeenCalledWith('o'); }); // 测试筛选逻辑正确性 it('should filter data correctly when input value is provided', (done) => { // 初始空值应返回全部数据 component.filteredNames.subscribe(result => { expect(result.length).toBe(3); done(); }); // 输入'o',筛选出One、Two component.form.get('name')?.setValue('o'); component.filteredNames.subscribe(result => { expect(result.length).toBe(2); expect(result.map(item => item.name)).toEqual(['One', 'Two']); done(); }); // 输入'Th',筛选出Three component.form.get('name')?.setValue('Th'); component.filteredNames.subscribe(result => { expect(result.length).toBe(1); expect(result[0].name).toBe('Three'); done(); }); }); // 模拟用户输入,验证DOM层面触发筛选 it('should filter options when user types in input', () => { const inputElement = fixture.nativeElement.querySelector('input[formControlName="name"]'); inputElement.value = 'o'; inputElement.dispatchEvent(new Event('input')); fixture.detectChanges(); const filteredResult = component.getFilterData('o'); expect(filteredResult.length).toBe(2); expect(filteredResult.map(item => item.name)).toEqual(['One', 'Two']); }); });
关键修正点
- 修复原HTML中
*ngFor绑定错误:将data改为filteredNames | async,确保绑定筛选后的异步数据 - 移除不必要的
HttpClientTestingModule导入 - 使用
done()回调处理Observable异步断言,避免测试提前结束 - 模拟真实输入事件(
input事件),覆盖DOM交互场景的代码路径
内容的提问来源于stack exchange,提问作者Yashwanth
相关产品推荐
相关产品推荐

