Angular NgModel two-way binding unit test

bmd picture bmd · Apr 3, 2017 · Viewed 7.2k times · Source

I'm attempting to test the two-way binding feature in Angular 2. I've also read through a few other answers but I still can't get the test to pass.

When the input field is updated, I would like to run a test that ensure the searchQuery property on the AppComponent class is the same as the value of the input field.

As mentioned, I've read a few other answers and as I've gone along included additional pieces of code. So what is there currently might not all be needed?

Component

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

@Component({
  selector: 'app-root',
  template: '<input type="text" name="input" [(ngModel)]="searchQuery" (change)="onChange()" id="search">',
  styles: ['']
})
export class AppComponent {
    public searchQuery: string;

    onChange() {
        console.log(this.searchQuery);
    }

}

Unit test

import { ComponentFixture, TestBed, async, fakeAsync, tick, ComponentFixtureAutoDetect } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';

describe('AppComponent', () => {
  let comp: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

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

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

  it('should create the app', fakeAsync(() => {
    const de = fixture.debugElement.query(By.css("#search"));
    const el = de.nativeElement;

    el.value = "My string";

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

    tick();

    fixture.detectChanges();

    expect(comp.searchQuery).toEqual("My string");
  }));
});

If there is a better approach, I am of course happy to get any feedback around this.

Answer

yurzui picture yurzui · Apr 3, 2017

You have to run

fixture.detectChanges();

before dispatching event to ensure that your control has initialized and registered onChange event

setUpControl function

// view -> model
dir.valueAccessor.registerOnChange(function (newValue) {
    dir.viewToModelUpdate(newValue);
    control.markAsDirty();
    control.setValue(newValue, { emitModelToViewChange: false });
});

Plunker Example

See also