How to test form.valueChanges in Angular?

10,051

You want to test changes on a form without inputs. Maybe try with this :

this.searchForm = this.fb.group({description: ['your_input']});

.

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [...],
    providers: [
      { provide: Store, useValue: MOCK_STORE },
    ],
    declarations: [FormComponent],
    schemas: [NO_ERRORS_SCHEMA]
  })
    .compileComponents();
}));

beforeEach(() => {
    fixture = TestBed.createComponent(FormComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
});

it('should dispatch action for valueChanges', () => {
    const spy = spyOn(TestBed.get(Store<AppState>), 'dispatch') 
    component.searchForm.controls['your_input'].setValue('test') // This will trigger change
    expect(spy).toHaveBeenCalledTimes(1);
});
Share:
10,051
Felix
Author by

Felix

Updated on June 24, 2022

Comments

  • Felix
    Felix about 2 years

    How to correctly unit test (Karma, Jasmine), that emmisions of valueChanges dispatches a FormUpdated action?

    beforeEach(async(() => {
      TestBed.configureTestingModule({
        imports: [...],
        providers: [
          { provide: Store, useValue: MOCK_STORE },
        ],
        declarations: [FormComponent],
        schemas: [NO_ERRORS_SCHEMA]
      })
        .compileComponents();
    }));
    
    beforeEach(() => {
        fixture = TestBed.createComponent(FormComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });
    
    export class FormComponent implements OnInit {
        searchForm: FormGroup;
    
        constructor(private readonly fb: FormBuilder, private readonly store: Store<AppState>) {
        }
    
        ngOnInit(): void {
            this.searchForm = this.fb.group({});
            this.searchForm.valueChanges.subscribe(value => this.store.dispatch(new FormUpdated(value)));
        }
    }
    

    I have tried something like this:

    it('should dispatch action for valueChanges', () => {
        const spy = spyOn(component['store'], 'dispatch');
        spyOn(component.searchForm, 'valueChanges').and.returnValue(of({}));
    
        expect(spy).toHaveBeenCalledTimes(1);
    });
    

    But this does not work - the spy has not been called.

    [Edit1] - Based on comments and answer:

    The problem is with the test asynchronicity. Some part of ngOnInit calls setTimeout(() => this.searchForm.get('field').updateValueAndValidity();)) which causes an emmision to this.searchForm.valueChanges() so the this.store.dispatch is actually called but after the expect(spy).toHaveBeenCalledTimes(1).

    I have tried to add fakeAsync(), tick() and flushMicrotasks() but with same outcome.

    it('should dispatch action for valueChanges', () => {
        const spy = spyOn(component['store'], 'dispatch');
        spyOn(component.searchForm, 'valueChanges').and.returnValue(of({}));
    
        tick();
        flushMicrotasks();
    
        expect(spy).toHaveBeenCalledTimes(1);
    });
    
  • malvadao
    malvadao over 5 years
    component.searchForm.controls['your_input'].setValue('test') this has successfully triggered the valueChange. Thank you!