4 ways to test button click event handler in Angular unit testing


In this tutorial, You learned unit testing button click event in Angular application.

In my previous post, Discussed about Angular button click event example

Angular unit test button click event example

Let’s define an angular component - ButtonDemoComponent for button click event In this component, Button displayed and on clicking the button, Click event is called and displays the message to UI element.

<div style="text-align:center">
    <h1>
        Angular Button Click Event Example
    </h1>
    <button (click)="clickEvent()">
        Click Me</button>
    <h2>{{msg}}</h2>
</div>

Typescript component

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

@Component({
  selector: 'app-button-demo',
  templateUrl: './button-demo.component.html',
  styleUrls: ['./button-demo.component.scss']
})
export class ButtonDemoComponent implements OnInit {
  msg: string = '';
  constructor() { }
  ngOnInit(): void {
  }
  clickEvent() {
    this.msg = 'Button is Clicked';
    return this.msg;
  }
}

There are multiple ways we can check events in Angular applications

Button click an events, events are browser based asynchronous in nature, So you can use with or with out async and fakeAscyn functions.

First get the instance of an component in beforeEach of spec.ts file

  import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { ButtonDemoComponent } from './button-demo.component';

describe('ButtonDemoComponent', () => {
  let component: ButtonDemoComponent;
  let fixture: ComponentFixture<ButtonDemoComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ButtonDemoComponent]
    })
      .compileComponents();
  });

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

Let’s see an example without asynchronous behaviour

Jasmine spyOn click event example

jasmine API provides spyOn method to call methods of an class or components.

It is used to get methods with or with out arguments to create stub of the method

Syntax

spyOn("instance","method")

Here instance is an component or a class which we can get using TestBed.createComponent(ButtonDemoComponent) method We can supply parameters as well as return types for spyon method

Here is an spyon example how it is created for arguments and return type

spyOn(Calculator, 'add')
.withArgs(1, 9).and.returnValue(10)
.withArgs(0, 1).and.returnValue(1)
.withArgs(undefined, undefined).and.returnValue(0);

We have created a spy or stub for a add method

It returns the possible values for executing add method with Calculator.add() method Add method will not do real addition logic but we are definding what values returned based on arguments.

Calculator.add(undefined,undefined)  returns 0
Calculator.add(1,9)  returns 10
Calculator.add(0,1)  returns 1

spyon method is useful on a method when we have a dependencies inside a method logic like subscribe and API calls. Let’s see step by step calls for button even

  • create a stub object using spyon on given function - clickEvent
  • call actual function component.clickEvent() which not calls function but calls the stub object
  • get buton selector using By.css selector
  • Finally, jasmine matcher toHaveBeenCalled checked against method is called or not.
  it('Button click event using spyon', () => {
    spyOn(component, 'clickEvent');
    component.clickEvent();

    fixture.detectChanges();

    let buton = fixture.debugElement.query(By.css('button')).nativeElement.click();
    expect(component.clickEvent).toHaveBeenCalled();
  });

triggerEventHandler in unit testing angular

triggerEventHandler is an function event available in DebugElement in Angular testing.

It triggers an event by name on a DOM Object and corresponding event handler is called on the native element.

When testing code, You have to call triggerEventHandler after detectChanges and before assertion.

    it('button click event triggerEventHandler ', () => {
    spyOn(component, 'clickEvent'); // attach events to component with bining
    let btn = fixture.debugElement.query(By.css('button'));
    btn.triggerEventHandler('click', null);
    fixture.detectChanges();
    expect(component.clickEvent).toHaveBeenCalled();
  });

Steps

  • Create a stub object with spyon function
  • get Button object using DebugElement with By API
  • call event handler function with triggerEventHandler
  • Finally Assert event handler function is called or not toHaveBeenCalled() in expect function

async button click event testing example

In this approach, used async function from Angular testing.

This helps to create a asynchronous function, inside it, all asynchronous functions are written and executed.

  • create a stub object clickevent function
  • get Button element and called using click event or triggerevent handler
  • detectChanges executes detection change event for an component.
  • whenStable function is defined in ComponentFixture which gives promise object. It waits for all tasks to be completed in ngZone, withouts using this, it calls immediately
 it('button click event one way', async(() => {
    spyOn(component, 'clickEvent');

    let button = fixture.debugElement.nativeElement.querySelector('button');
    button.click(); // you can use     btn.triggerEventHandler('click', null);

    fixture.detectChanges();

    fixture.whenStable().then(() => {
      expect(component.clickEvent).toHaveBeenCalled();
    });
  }));

fakeAsync with tick method

tick method is defined in Angular testing api. It is always used with fakeAync only. It waits for time to finish all pending tasks fakeAsync is used to run the test code in asynchronous test zone

  import { fakeAsync, tick } from '@angular/core/testing';

Here is an example using fakeAsync and tick for button click event hanlder

    it('button click event triggerEventHandler ', fakeAsync(() => {
    fixture.detectChanges();
    spyOn(component, 'clickEvent'); //method attached to the click.
    let btn = fixture.debugElement.query(By.css('button'));
    btn.triggerEventHandler('click', null);
    tick(); 
    fixture.detectChanges();
    expect(component.clickEvent).toHaveBeenCalled();
  }));

Angular button click event hander complete example

Here is an complete testing code for button click event testing.

import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import { ButtonDemoComponent } from './button-demo.component';

describe('ButtonDemoComponent', () => {
  let component: ButtonDemoComponent;
  let fixture: ComponentFixture<ButtonDemoComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ButtonDemoComponent]
    })
      .compileComponents();
  });

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('button click event one way', async(() => {
    spyOn(component, 'clickEvent');

    let button = fixture.debugElement.nativeElement.querySelector('button');
    button.click();
    fixture.detectChanges();

    fixture.whenStable().then(() => {
      expect(component.clickEvent).toHaveBeenCalled();
    });
  }));
  it('button click event triggerEventHandler ', fakeAsync(() => {
    fixture.detectChanges();
    spyOn(component, 'clickEvent');
    let btn = fixture.debugElement.query(By.css('button'));
    btn.triggerEventHandler('click', null);
    tick();
    fixture.detectChanges();
    expect(component.clickEvent).toHaveBeenCalled();
  }));
  it('button click event triggerEventHandler ', () => {
    fixture.detectChanges();
    spyOn(component, 'clickEvent');
    let btn = fixture.debugElement.query(By.css('button'));
    btn.triggerEventHandler('click', null);
    fixture.detectChanges();
    expect(component.clickEvent).toHaveBeenCalled();
  });
  it('Button click event using spyon', () => {
    spyOn(component, 'clickEvent');
    component.clickEvent();

    fixture.detectChanges();

    let buton = fixture.debugElement.query(By.css('button')).nativeElement.click();
    expect(component.clickEvent).toHaveBeenCalled();
  });

});

Conclusion

You learned multiple ways to test event hander for button click event in Angular unit test

  • spyon
  • triggerEventHandler
  • async and whenstable fixture
  • fakeAync and tick methods
THE BEST NEWSLETTER ANYWHERE
Join 6,000 subscribers and get a daily digest of full stack tutorials delivered to your inbox directly.No spam ever. Unsubscribe any time.

Similar Posts
Subscribe
You'll get a notification every time a post gets published here.