简体   繁体   中英

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

I am trying to test interactions between a components internals and its HTML.

In the following test code, I have an app-component with an input-field, which is linked to an attribute within the component using two-way binding.

Test1 passes with the use of fixture.detectChanges and waiting it to be stable. So, in this test the components test-attribute is synchronised with the input-field.

However, test2 fails due to the fact that the compontent has not been updated with the new test-attribute value. So, what am I missing or doing wrong to synchronise the changes before entering into the assertion?

 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>

I took the answer below and looked at the minimal from the answer to get my test to pass. The key seems to have been to move the fixture.detectChanges() to the beforeEach() function.

I believe this was the minimum I needed to include for a passing test, in addition to adding the fixture.detectChanges in the beforeEach. It seems that the done and the fixture.whenStable is not necessary.

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

I think the issue with your test is that you never initially call fixture.detectChanges() after you set up your fixture and component in your beforeEach .

Check this stackblitz for a working version of your test.

I added a second beforeEach where fixture.detectChanges is called:

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

And here is the failing (now working) test:

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

In the second test, you have to add this:

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

before doing the expect, and not after

and then you have also to add

fixture.detectChanges(); before doing the expect as well

so write your second test like this:

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

You have to add async to your it function because ngModel is async.

Alternatively you can use fakeAsync as well

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM