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

1 Answer 1

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
Sign up to request clarification or add additional context in comments.

4 Comments

Fantastic answer. Thank you!
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()?
@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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.