Mattes83
Mattes83

Reputation: 31

Angular 2 unit testing with ngModel

I am using the latest angular-cli with angular 2.1. The component test I want to write is simple: Get some data from a service and display it.

It works, when the data ist simply displayed in a paragraph, but it does not, when it is bound to a textarea.

Below is an excerpt from my template:

<p *ngIf="!isBusy" class="twain">
  <i>{{settings.blockLoginMessage}}</i>
</p>
<textarea  
  id="blockLoginMessage" 
  name="blockLoginMessage" 
  [(ngModel)]="settings.blockLoginMessage" 
  class="form-control">
</textarea>

and the corresponding tests:

describe('SettingsComponent', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [SharedModule, TranslateModule.forRoot()],
            declarations: [SettingsComponent],
            providers: [
                Overlay,
                ToastsManager,
                ViewContainerRef,
                TranslateService,
                SettingsService
            ]
        });

        fixture = TestBed.createComponent(SettingsComponent);
        comp = fixture.componentInstance;

        settingsService = fixture.debugElement.injector.get(SettingsService);

        getSettingsSpy = spyOn(settingsService, 'getSettings')
            .and.returnValue(Observable.of(new Settings(true, false, 'test')).delay(500).toPromise());

        setSettingsSpy = spyOn(settingsService, 'setSettings')
            .and.returnValue(Observable.of(true).delay(500));

        fixture.detectChanges(); 
    });

    it('should create the component', () => {
        expect(comp).toBeTruthy();
    });

    it('should show busy indicator as long as there are no settings', () => {
        let busyPanel = fixture.debugElement.query(By.css('#busyPanel'));
        expect(busyPanel).not.toBeNull('cant find busy');
    });

    it('should show loaded settings after observable finished (done)', done => {
        fixture.detectChanges();

        // get the spy promise and wait for it to resolve
        getSettingsSpy.calls.mostRecent().returnValue.then(() => {
            fixture.detectChanges();

            let de = fixture.debugElement.query(By.css('.twain'));
            let el = de.nativeElement;

            expect(el.textContent).toBe('test', 'show loaded settings');

            let de2 = fixture.debugElement.query(By.css('#blockLoginMessage'));
            let el2: HTMLTextAreaElement = de2.nativeElement;

            expect(el2.textContent).toBe('test', 'show loaded settings');

            done();
        });
    });
});

Does anyone have a hint why this does not work?

Furthermore, my attempts with async and fakeAsync fail completely, when I have a delay in my promises like above.

Upvotes: 3

Views: 3815

Answers (1)

Dima Kuzmich
Dima Kuzmich

Reputation: 1326

ngModel updates asynchronously since RC5. You should test it asynchronous way:

it('should check initial value of ngModel', async(() => {
  let fixture = TestBed.createComponent(TestComponent);
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    let el = fixture.debugElement.query(By.css('select your input'));
    let actual = selectElement.nativeElement.value;
    expect(expected).toBe(actual);
  });
}));

Upvotes: 8

Related Questions