Unit testing click event in Angular Unit testing click event in Angular angular angular

Unit testing click event in Angular


My objective is to check if the 'onEditButtonClick' is getting invoked when the user clicks the edit button and not checking just the console.log being printed.

You will need to first set up the test using the Angular TestBed. This way you can actually grab the button and click it. What you will do is configure a module, just like you would an @NgModule, just for the testing environment

import { TestBed, async, ComponentFixture } from '@angular/core/testing';describe('', () => {  let fixture: ComponentFixture<TestComponent>;  let component: TestComponent;  beforeEach(async(() => {    TestBed.configureTestingModule({      imports: [ ],      declarations: [ TestComponent ],      providers: [  ]    }).compileComponents().then(() => {      fixture = TestBed.createComponent(TestComponent);      component = fixture.componentInstance;    });  }));});

Then you need to spy on the onEditButtonClick method, click the button, and check that the method was called

it('should', async(() => {  spyOn(component, 'onEditButtonClick');  let button = fixture.debugElement.nativeElement.querySelector('button');  button.click();  fixture.whenStable().then(() => {    expect(component.onEditButtonClick).toHaveBeenCalled();  });}));

Here we need to run an async test as the button click contains asynchronous event handling, and need to wait for the event to process by calling fixture.whenStable()

Update

It is now preferred to use fakeAsync/tick combo as opposed to the async/whenStable combo. The latter should be used if there is an XHR call made, as fakeAsync does not support it. So instead of the above code, refactored, it would look like

it('should', fakeAsync(() => {  spyOn(component, 'onEditButtonClick');  let button = fixture.debugElement.nativeElement.querySelector('button');  button.click();  tick();  expect(component.onEditButtonClick).toHaveBeenCalled();}));

Don't forget to import fakeAsync and tick.

See also:


Events can be tested using the async/fakeAsync functions provided by '@angular/core/testing', since any event in the browser is asynchronous and pushed to the event loop/queue.

Below is a very basic example to test the click event using fakeAsync.

The fakeAsync function enables a linear coding style by running the test body in a special fakeAsync test zone.

Here I am testing a method that is invoked by the click event.

it('should', fakeAsync( () => {    fixture.detectChanges();    spyOn(componentInstance, 'method name'); //method attached to the click.    let btn = fixture.debugElement.query(By.css('button'));    btn.triggerEventHandler('click', null);    tick(); // simulates the passage of time until all pending asynchronous activities finish    fixture.detectChanges();    expect(componentInstance.methodName).toHaveBeenCalled();}));

Below is what Angular docs have to say:

The principle advantage of fakeAsync over async is that the test appears to be synchronous. There is no then(...) to disrupt the visible flow of control. The promise-returning fixture.whenStable is gone, replaced by tick()

There are limitations. For example, you cannot make an XHR call from within a fakeAsync


I'm using Angular 6. I followed Mav55's answer and it worked. However I wanted to make sure if fixture.detectChanges(); was really necessary so I removed it and it still worked. Then I removed tick(); to see if it worked and it did. Finally I removed the test from the fakeAsync() wrap, and surprise, it worked.

So I ended up with this:

it('should call onClick method', () => {  const onClickMock = spyOn(component, 'onClick');  fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null);  expect(onClickMock).toHaveBeenCalled();});

And it worked just fine.