19

I have a text input and I'm listening for the changes.

component

name = new FormControl('',Validators.required);

ngOnInit() {
  this.data = 'oldvalue';
  this.checkName();
}

checkName() {
  this.name.valueChanges.subscribe(val=>{
     console.log(val);
     this.data= "newvalue"; // updating Value
  });
}

HTML

<input name="name" formControlName="name">

My Attempt so far:

component.spec.ts

it('should test data field ', () => {
    const fixture = TestBed.createComponent(UserComponent);
    const app=fixture.debugElement.componentInstance;
    const el = fixture.nativeElement.querySelector('input');
    el.value ='something';
    dispatchEvent(new Event(el));
    fixture.detectChanges();
    fixture.whenStable().then(()=>{expect(app.data).toBe('newvalue');
});

Problem: Even though input field is populated the code inside subscribe callback is never executed.

It always shows:

Expected 'oldvalue' to be 'newvalue'.

I tried setValue() method too but it did not work. it never goes inside subscribe callback

app.name.setValue('vikas');
fixture.detectChanges();
fixture.whenStable().then(()=>{expect(app.data).toBe('newvalue');

I referred Updating input html field from within an Angular 2 test and Angular2 Component: Testing form input value change but no luck :(

What am I missing?

1 Answer 1

37

At first glance I think you missed the fact that your FormControl is not connected to input because you're using FormControlName directive that takes control name as @Input.

If you want to test FormControl then you can consider FormControlDirective that takes FormControl as @Input:

<input name="name" [formControl]="name">
                                  ^^^^^
                      `name` is FormControl instance here not string

Now we can be sure that whenever we change text in input your FormControl will fire changes. But as soon as you write such template angular will ask you for ReactiveFormsModule dependency in your test:

import { ReactiveFormsModule } from '@angular/forms';
....

TestBed.configureTestingModule({
   imports: [
     ReactiveFormsModule  <=== add this
   ],
   declarations: [TestComponent],
});

Now regarding your test.

1) You must tell the TestBed to perform data binding by calling fixture.detectChanges():

const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges(); <== add this

2) You should fire change on input correctly:

el.dispatchEvent(new Event('input'));

Here's the whole code:

it('should display original title', () => {
  const fixture = TestBed.createComponent(TestComponent);
  fixture.detectChanges();
  const app = fixture.debugElement.componentInstance;
  const el = fixture.nativeElement.querySelector('input');
  el.value = 'something';
  el.dispatchEvent(new Event('input'));
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    expect(app.data).toBe('newvalue');
  });
});

Plunker Example

Sign up to request clarification or add additional context in comments.

4 Comments

Thanx Mate Worked like a charm :)
Hi, I'm getting an issue with this approach, please can you see this question stackoverflow.com/questions/58355726/…
I'm using with the last Angular version and Jest, I had to await for the fixture.whenStable(), then do the assert
for single formcontrol and based on input , it works fine. But if we have multiple formcontrols and all are in same input , how to differentiate each one.

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.