question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unit test: a text input value is not updated without the value attribute when a two-way bindinging used

See original GitHub issue

I’m submitting a…


[x] Bug report  

Current behavior

I test a component containing a text input. There is a two-way binding between the input element and a text property of a component class via ngModel directive.

The test updates the input’s value, fires dispatchEvent and checks the value of the input. The test fails:


Error: Expected 'Default title' to equal 'Updated Task'.
    at stack (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?73bc53d3671677e6a093fc74e4f9bcde57e5f7ad:2338:17)
    at buildExpectationResult (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?73bc53d3671677e6a093fc74e4f9bcde57e5f7ad:2308:14)
    at Spec.expectationResultFactory (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?73bc53d3671677e6a093fc74e4f9bcde57e5f7ad:858:18)
    at Spec.addExpectationResult (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?73bc53d3671677e6a093fc74e4f9bcde57e5f7ad:487:34)
    at Expectation.addExpectationResult (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?73bc53d3671677e6a093fc74e4f9bcde57e5f7ad:802:21)
    at Expectation.toEqual (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?73bc53d3671677e6a093fc74e4f9bcde57e5f7ad:2252:12)
    at http://localhost:9876/_karma_webpack_/webpack:/src/app/app.component.spec.ts:33:34
    at ZoneDelegate.webpackJsonp../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:388:1)
    at AsyncTestZoneSpec.webpackJsonp../node_modules/zone.js/dist/zone-testing.js.AsyncTestZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:479:1)
    at ProxyZoneSpec.webpackJsonp../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:236:1)

NOTE: If I add the value attribute to the input element in the app.component.html then the test passes.:

<input name="title" type="text" [(ngModel)]="title" value={{title}}> 

Expected behavior

Chrome 64.0.3282 (Windows 7.0.0): Executed 1 of 1 SUCCESS

Minimal reproduction of the problem with instructions

1. Create project:

ng new angular-app

2. Update files:

app.component.html - replace all the text with the following:

<input name="title" type="text" [(ngModel)]="title">

app.component.ts - replace all the text with the following:

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Default title';
}

app.component.spec.ts - replace all the text with the following:

import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {FormsModule} from '@angular/forms';

import {AppComponent} from './app.component';
import {By} from "@angular/platform-browser";

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      imports: [FormsModule],
    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(AppComponent);
      comp = fixture.debugElement.componentInstance;
    })
  }));

  it('should bind an input to a property (async)', async(() => {
    fixture.detectChanges();

    // Update the title input
    const inputElement = fixture.debugElement.query(By.css('input[name="title"]')).nativeElement;
    inputElement.value = 'Updated Task';
    inputElement.dispatchEvent(new Event('input'));

    fixture.whenStable().then(() => {
      fixture.detectChanges();
      expect(inputElement.value).toEqual('Updated Task');
    });
  }));
});

app.module.ts - replace all the text with the following:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

What is the motivation / use case for changing the behavior?

I need to test a component with a form inside.

3. Run tests:

ng test

Environment


Angular CLI: 1.7.2
Node: 8.5.0
OS: win32 x64
Angular: 5.2.7
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cli: 1.7.2
@angular-devkit/build-optimizer: 0.3.2
@angular-devkit/core: 0.3.2
@angular-devkit/schematics: 0.3.2
@ngtools/json-schema: 1.2.0
@ngtools/webpack: 1.10.1
@schematics/angular: 0.3.2
@schematics/package-update: 0.3.2
typescript: 2.5.3
webpack: 3.11.0

Browser:
- [*] Chrome (desktop) version XX

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:3
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

5reactions
karacommented, Mar 29, 2018

@vduzh Values are set on ngModel asynchronously. The test sets the value after fixture.detectChanges but before the fixture has become stable from its initial run. So when you call whenStable, the value is set to Default title for the first time. You’ll need to move the whenStable up. I suspect you also want to test the value in the class, rather than checking the native element value you just set.

There are a bunch of examples of ngModel value setting in the spec for template-driven forms here, if that helps:

https://github.com/angular/angular/blob/master/packages/forms/test/template_integration_spec.ts#L29

3reactions
NagRockcommented, Sep 7, 2018

@kara await fixture.whenStable() did the job. Thanks!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Updating input html field from within an Angular 2 test
I can grab the DebugElement and the nativeElement of the input field without a problem. (Just setting a the value property on the...
Read more >
Unit testing ngModel in Angular 4
In the test case shown above, we did following: First, we queried the input element and then updated its value. Next, we dispatched...
Read more >
Testing Attribute Directives
The <input> case binds the HighlightDirective to the name of a color value in the input box. The initial value is the word...
Read more >
Layouts and binding expressions
android:text="@{user.lastName}"/> </LinearLayout> </layout>. The user variable within data describes a property that may be used within this ...
Read more >
Template-Driven Forms • Angular - codecraft.tv
The forms value is an empty object, even if you started typing into the input fields the value would not update. This is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found