You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
  });
});

修正后的完整测试用例

核心测试逻辑说明

  1. 验证输入值变更时筛选函数被触发
  2. 验证筛选逻辑的正确性(不同输入对应不同筛选结果)
  3. 模拟用户真实输入行为,验证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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:21:26