Unit test: a text input value is not updated without the value attribute when a two-way bindinging used
See original GitHub issueI’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:
- Created 6 years ago
- Reactions:3
- Comments:9 (2 by maintainers)
Top 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 >
Top Related Medium Post
No results found
Top Related StackOverflow Question
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@vduzh Values are set on
ngModel
asynchronously. The test sets the value afterfixture.detectChanges
but before the fixture has become stable from its initial run. So when you callwhenStable
, the value is set toDefault title
for the first time. You’ll need to move thewhenStable
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
@kara
await fixture.whenStable()
did the job. Thanks!