angular 交互测试中的变化检测

angular change detection in interaction test

我有一个简单的反应形式 (angular 11)

<form class="searchForm" [formGroup]="form">
  <input formControlName="firstName" required/>
  <button [disabled]="! form.valid">Submit</button>
</form>
@Component({
  selector: 'app-simple-form',
  templateUrl: './my-form.component.html',
  styleUrls: ['./my-form.component.css']
})
export class MyFormComponent {

  form: FormGroup;

  constructor() {
    this.form = new FormGroup({
      firstName: new FormControl()
    });
  }
}

我正在尝试做一个简单的交互测试,我正在设置一个值 在所需的文本字段中,触发输入事件。我期待着 在事件 angular 运行更改检测并更新 按钮的禁用状态,但这不会发生。我必须触发 手动更改检测以使按钮具有正确的禁用状态。 我对这种行为感到有些困惑。

 it('should enable submit button', () => {

    const input: HTMLInputElement = document.querySelector('input[formControlName="firstName"]');
    const button: HTMLButtonElement = document.querySelector('button');

    // simulate user interaction
    input.focus();
    input.value = 'john';

    expect(component.form.valid).toBeFalse();
    expect(button.disabled).toBeTruthy();

    const event = new Event('input', {
      bubbles: true,
      cancelable: true,
    });
    input.dispatchEvent(event);

    // required field has text -> form valid
    expect(component.form.valid).toBeTrue();

    // but button is still disabled
    expect(button.disabled).toBeTrue();

    fixture.detectChanges();

    // only after detectChanges the disabled state is correct
    expect(button.disabled).toBeFalse();

  });

默认情况下,您必须在 Angular 规范测试中手动调用更改检测。 这在 Angular docs "Component testing scenarios".

中进行了解释

In production, change detection kicks in automatically when Angular creates a component or the user enters a keystroke or an asynchronous activity (e.g., AJAX) completes.

Delayed change detection [in tests] is intentional and useful. It gives the tester an opportunity to inspect and change the state of the component before Angular initiates data binding and calls lifecycle hooks.

您可以在 Angular 规范测试中启用自动更改检测。 如果您不想处理手动调用更改检测(因为您需要经常调用它或者觉得它对您的场景没有用),这可能很有用。通过使用 ComponentFixtureAutoDetect 提供程序配置 TestBed,可以启用自动更改检测

TestBed.configureTestingModule({
  declarations: [ MyFormComponent ],
  providers: [
    { provide: ComponentFixtureAutoDetect, useValue: true }
  ]
});

但是,请注意使用此提供程序有一些限制。

The ComponentFixtureAutoDetect service responds to asynchronous activities such as promise resolution, timers, and DOM events.

所以这应该适用于您的情况,因为您正在通过 input.dispatchEvent() 调用执行 DOM 交互。但是,如果你想直接更新一个没有DOM交互的组件,你需要调用detectChanges().

But a direct, synchronous update of the component property is invisible. The test must call fixture.detectChanges() manually to trigger another cycle of change detection.

it('should display updated title after detectChanges', () => {
  comp.title = 'Test Title';
  fixture.detectChanges(); // detect changes explicitly to see direct component update.
  expect(h1.textContent).toContain(comp.title);
});

有关详细信息,请参阅 Automatic change detection in Angular docs