使用 [(ngModel)] 不会对 ngFor 内部的输入应用更改单元测试

Ric*_*key 4 jasmine karma-jasmine angular

我在测试一个 Angular 组件时遇到问题,该组件[(ngModel)]ngFor. 它在实际应用中运行良好。这只是测试的问题。

这是一个失败的示例测试:

import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { Component, EventEmitter, Output } from '@angular/core';
import { FormsModule } from '@angular/forms';

describe('Example Test', () => {
  @Component({
    template: `
      <input *ngFor="let value of values"
             type="checkbox"
             class="checkbox-1"
             [(ngModel)]="value.isSelected"
             (change)="output.emit(values)">
    `,
    styles: [``]
  })
  class TestHostComponent {
    @Output() output: EventEmitter<any> = new EventEmitter();

    values = [
      { isSelected: true },
      { isSelected: true },
      { isSelected: true },
    ];
  }

  let testHost: TestHostComponent;
  let fixture: ComponentFixture<TestHostComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [FormsModule],
      declarations: [TestHostComponent],
      providers: []
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestHostComponent);
    testHost = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should change isSelected', fakeAsync(() => {
    const spy = spyOn(testHost.output, 'emit');
    fixture.nativeElement.querySelectorAll('.checkbox-1')[0].click();
    fixture.detectChanges();
    tick();

    expect(spy).toHaveBeenCalledWith([
      { isSelected: false }, // it fails because this is still true
      { isSelected: true },
      { isSelected: true },
    ]);
  }));
});
Run Code Online (Sandbox Code Playgroud)

[(ngModel)]在类似的测试中,使用不在循环中的单个输入可以正常工作。我甚至从记录的发射值(ngModelChange),并单击该复选框时$eventtrue当它真的应该false

有任何想法吗?

cod*_*iet 6

似乎执行点击的方法不会触发更改检测。在复选框上调度更改事件反而给出了预期的结果:

it('should change isSelected', fakeAsync(() => {
    const spy = spyOn(testHost.output, 'emit');
    const checkbox = fixture.nativeElement.querySelectorAll('.checkbox-1')[0];
    checkbox.dispatchEvent(new Event('change'));
    fixture.detectChanges();
    tick();

    expect(spy).toHaveBeenCalledWith([
        { isSelected: false }, // This is now false
        { isSelected: true },
        { isSelected: true },
    ]);
}));
Run Code Online (Sandbox Code Playgroud)

这篇文章启发的解决方案。

更新:

看起来需要等待一些控件在 CheckboxControlValueAccessor 上初始化或注册。如果beforeEach()在创建组件后修改第二个等待一个循环,则原始测试代码有效:

beforeEach(fakeAsync(() => {
    fixture = TestBed.createComponent(TestHostComponent);
    testHost = fixture.componentInstance;
    fixture.detectChanges();
    tick();
}));
Run Code Online (Sandbox Code Playgroud)

有关完整答案/解释,请参阅此 Github 问题