Search code examples
angulartypescripttesting2-way-object-databinding

How to test Angular 2 two way binding input value


I am trying to write a test for my component to test that angular two way binding is working.

On one side I have a test that looks like that (and it passes):

it('should bind displayed value to the SearchComponent property', () => {
    searchComponent.searchBoxValue = 'abc';
    searchCompFixture.detectChanges();
    expect(inputEl.nativeElement.getAttribute('ng-reflect-model')).toBe('abc');
});

where

searchCompFixture = TestBed.createComponent(SearchComponent);
inputEl = searchCompFixture.debugElement.query(By.css('#search-box-input'));

and

<input
    id="search-box-input"
    [(ngModel)]="searchBoxValue"\>

On the other hand I want to write a test that sets the value of the input element first and checks that SearchComponent property value has changed. My attempt:

it('should bind SearchComponent property to the displayed value', fakeAsync(() => {
    inputEl.nativeElement.value = 'abc';
    let evt = new Event('input');
    inputEl.nativeElement.dispatchEvent(evt);

    tick();

    searchCompFixture.detectChanges();
    expect(searchComponent.searchBoxValue).toBe('abc');
}));

but this doesn't work because searchComponent.searchBoxValue value does not get set. Any ideas how to fix this?


Solution

  • It turns out that you need to detechtChanges before updating the value of the input field (idk why). Here is the working test:

    it('should bind SearchComponent property to the displayed value', fakeAsync(() => {
        searchCompFixture.detectChanges();
    
        inputEl.nativeElement.value = 'abc';
        let event = new Event('input');
        inputEl.nativeElement.dispatchEvent(event);
    
        tick();
        expect(searchCompFixture.componentInstance.searchBoxValue).toEqual('abc');
    }));
    

    EDIT: I found another improvement for the test should bind displayed value to the SearchComponent property. What I didn't like about it was that I used weird angular attribute ng-reflect-model and not the normal way inputEl.nativeElement.value. The problem with this was that value does not get set yet by angular.

    Changing the test to the following solves the problem and no magic needed anymore - hoorah!

    it('should bind displayed value to the SearchComponent property', fakeAsync(() => {
        searchComponent.searchBoxValue = 'abc';
    
        searchCompFixture.detectChanges();
        tick();
        searchCompFixture.detectChanges();
    
    
        expect(inputEl.nativeElement.value).toBe('abc');
    }));