如何在断言之前将组件状态与 html-input 同步?

how to synchronise component state with html-input before assertion?

我正在尝试测试组件内部结构与其 HTML 之间的交互。

在下面的测试代码中,我有一个带有输入字段的应用程序组件,它使用双向绑定链接到组件内的一个属性。

Test1 使用 fixture.detectChanges 通过并等待它稳定。因此,在此测试中,组件测试属性与输入字段同步。

但是,由于组件尚未使用新的测试属性值更新,因此测试 2 失败。那么,在进入断言之前同步更改我错过了什么或做错了什么?

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  test = 'hello';
}

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import {BrowserModule, By} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';
import {ComponentFixture} from '../../node_modules/@angular/core/testing';
import {detectChanges} from '../../node_modules/@angular/core/src/render3';
describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        FormsModule
      ],
      declarations: [
        AppComponent,
      ],
    }).compileComponents();
      fixture = TestBed.createComponent(AppComponent);
      component = fixture.componentInstance;
    });
  it('test1', () => {
    const inputElement = fixture.debugElement.query(By.css('input'));
    const el = inputElement.nativeElement;
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      expect(el.value).toBe('hello');
    })

  });
  it('test2', () => {
    const inputElement = fixture.debugElement.query(By.css('input'));
    const el = inputElement.nativeElement;
    el.value = 'test';
    expect(el.value).toBe('test');
    el.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      expect(component.test).toBe('test');
    })
  });
});
<div>
  <input [(ngModel)]="test" type="text">
  <p>{{test}}</p>
</div>

我接受了下面的答案并查看了答案中的最小值以通过测试。关键似乎是将 fixture.detectChanges() 移动到 beforeEach() 函数。

我相信除了在 beforeEach 中添加 fixture.detectChanges 之外,这是通过测试所需包含的最低限度。看来 done 和 fixture.whenStable 是没有必要的。

it('test2', () => {
   const inputElement = fixture.debugElement.query(By.css('input'));
   const el = inputElement.nativeElement;
   el.value = 'test';
   el.dispatchEvent(new Event('input'));
   expect(component.test).toBe('test');
});

在第二次测试中,您必须添加:

el.dispatchEvent(new Event('input'));

在执行 expect 之前,而不是在

之后

然后你还必须添加

fixture.detectChanges(); 在做 expect 之前

所以这样写你的第二个测试:

it('test2', async(() => {
    const inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
    inputElement.value = 'test';
    inputElement.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      expect(component.test).toBe('test');
    })
}));

您必须将 async 添加到您的 it 函数 因为 ngModel 是异步的。

或者你也可以使用 fakeAsync

我认为您测试的问题在于,您在 beforeEach.[=19 中设置 fixturecomponent 后,最初从未调用 fixture.detectChanges() =]

Check this stackblitz for a working version of your test.

我添加了第二个 beforeEach,其中 fixture.detectChanges 被称为:

beforeEach(() => {
  fixture = TestBed.createComponent(AppComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
});

这是失败的(现在有效的)测试:

it('test2', (done) => {
  const inputElement = fixture.nativeElement.querySelector('input');
  inputElement.value = 'test';

  const inputEvent = document.createEvent('Event');
  inputEvent.initEvent('input', true, true);
  inputElement.dispatchEvent(inputEvent);

  fixture.detectChanges();
  fixture.whenStable().then(() => {
    expect(component.test).toBe('test');
    done();
  });
});