Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2+: Testing form with mat-slide-toggle - change event won't fire

I am not sure why the the change event isn't firing and therefore not triggering the function I registered for the change event.

I know this works when I run it, but I can't get it in the spec.

Here is a demo of what I am talking about... As you can see from the logs, the toggle never gets called: https://stackblitz.com/edit/angular-material-slide-toggle-test-utmbmy?file=app%2Fhello.component.spec.ts

Roughly, this is my code here...

component.html:

<mat-slide-toggle [checked]="useDefault" (change)="toggle($event)"></mat-slide-toggle>

component.ts:

...
toggle(event: MatSlideToggleChange) {
  console.log('Toggle fired');
  this.useDefault = event.checked;
}

In my spec.ts:

it('should trigger toggle...', () => {
  const componentDebug = fixture.debugElement;
  const slider: MatSlideToggle = 
     componentDebug.query(By.css('form mat-slide-toggle')).componentInstance;

  console.log('before ' + slider.checked);
  slider.toggle();

  fixture.detectChanges();

  console.log('after ' + slider.checked);
  console.log('useDefault ' + component.useDefault);
});

The after logging does print out slider.checked to be the opposite value from before. However, the component.useDefault stays the same and the logging statement in the toggle function is never called, which means it never triggered it.

Why is this going on and how do I fix this?

I've tried placing the whole thing into fakeAsync, using tick, placing the 'after' code into fixture.whenStable() and fixture.whenRenderingDone().

I have a NoopAnimationsModule in the TestBed's import.

like image 765
user1902183 Avatar asked Jun 06 '18 22:06

user1902183


2 Answers

When using third-party components, you should trust them to handle the event. However, you could trigger the event, using the triggereventhandler while you spyOn the method you want to be called.

Component

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

@Component({
    selector: 'hello',
    template: `
        <mat-slide-toggle
            [checked]="useDefault" (change)="toggle($event)"
        ></mat-slide-toggle>`
})
export class HelloComponent  {
    public useDefault = false;

    public toggle(event: MatSlideToggleChange) {
        console.log('toggle', event.checked);
        this.useDefault = event.checked;
    }
}

Test

it('should call change method on slide change', () => {
    const componentDebug = fixture.debugElement;
    const slider = componentDebug.query(By.directive(MatSlideToggle));
    spyOn(component, 'toggle'); // set your spy

    slider.triggerEventHandler('change', null); // triggerEventHandler

    expect(component.toggle).toHaveBeenCalled(); // event has been called
});

See the test passing on stackblitz: https://stackblitz.com/edit/angular-material-slide-toggle-test-spy-trigger-event?file=app/hello.component.spec.ts

like image 82
Leo Caseiro Avatar answered Sep 17 '22 19:09

Leo Caseiro


your component.html

    <mat-slide-toggle (change)="onToggle($event)">Compare</mat-slide-toggle>

In your component you can check

    event.checked = true || false;
like image 30
Jyotirmoy Upadhaya Avatar answered Sep 21 '22 19:09

Jyotirmoy Upadhaya