3

I can't seem to test an input box bound to an Angluar2 model. I'm a little new to this so please bear with me.

I have a very basic Angular2 component which contains an inputbox bound to a model.

<form>
    <input  type="text" [(ngModel)]="searchTerm" name="termsearchTerm" (keyup)="search()"  value=""/>
</form>

Here's the code behind:

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
    moduleId: module.id,
    selector: 'sc-search',
    templateUrl: 'search.component.html'
})

export class SearchComponent {
    @Input() data: any;
    @Output() onSearchTermChanged = new EventEmitter<string>();
    private searchTerm: string;

    search() {
        this.onSearchTermChanged.emit(this.searchTerm);
    }
}

When running the following test keep getting Expected undefined to equal 'John'. I'm expecting the test to pass.

 searchableHierarchyComponentFixture.detectChanges();

    let termInputDbg = searchableHierarchyComponentFixture.debugElement.query(By.css('input[name="termsearchTerm"]'));
    let input = searchableHierarchyComponentFixture.nativeElement.querySelector('input');

    input.value = 'John';

    let evt = document.createEvent('Event');
    evt.initEvent('keyup', true, false);

    termInputDbg.triggerEventHandler('keyup', null);
    input.dispatchEvent(evt);


    tick(50);
    searchableHierarchyComponentFixture.detectChanges();
    searchableHierarchyComponentFixture.whenStable();
    expect(searchableHierarchyComponentFixture.componentInstance.searchTerm).toEqual('John');
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
K-Dawg
  • 3,013
  • 2
  • 34
  • 52

1 Answers1

5

So a couple things:

  1. You need to call tick after the component is created. Not completely sure, but I think the model binding happens asynchronously. Without ticking, the test will fail.
  2. The directive listens for the input event, not the keyup. When the input event occurs, the directive updates the bindings.

Here's a complete test (also on Plunker)

import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

@Component({
  selector: 'test',
  template: `
    <form>
      <input type="text" [(ngModel)]="query" name="query" />
    </form>
  `
})
class TestComponent {
  query: string;
}

describe('TestComponent: ngModel', () => {

  let fixture;
  let component;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [FormsModule],
      declarations: [TestComponent]
    });
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
  });

  it('should change the value', fakeAsync(() => {
    let input = fixture.debugElement.query(By.css('input')).nativeElement;

    fixture.detectChanges();
    tick();

    expect(component.query).toBeFalsy();

    input.value = 'Hello World';
    input.dispatchEvent(new Event('input'));
    tick();

    expect(component.query).toBe('Hello World');

  }));
});

See also

  • When in doubt, sometimes the best place to look for examples is the source code tests
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Fantastic answer. Thank you! – K-Dawg Mar 01 '17 at 10:10
  • Hey guys, like an answer and the question itself. but what is tick function here? I have same situation now. and want to test ngModel bound to the input. do I need that tick()? – Janatbek Orozaly Aug 09 '17 at 17:29
  • @JanatbekSharsheyev For input test, yes you will need it. If you're not sure what it is , see the angular docs for testing. There's a section on asynchronous testing – Paul Samsotha Aug 09 '17 at 17:31
  • Paul's link has moved here https://github.com/angular/angular/blob/master/packages/forms/test/template_integration_spec.ts – mamacdon Nov 14 '18 at 20:51