Any way to test EventEmitter in Angular2?

Although the highest voted answers work, they are not demonstrating good testing practices, so I thought I would expand on Günter's answer with some practical examples.

Let's imagine we have the following simple component:

@Component({
  selector: 'my-demo',
  template: `
    <button (click)="buttonClicked()">Click Me!</button>
  `
})
export class DemoComponent {
  @Output() clicked = new EventEmitter<string>();

  constructor() { }

  buttonClicked(): void {
    this.clicked.emit('clicked!');
  }
}

The component is the system under test, spying on parts of it breaks encapsulation. Angular component tests should only know about three things:

  • The DOM (accessed via e.g. fixture.nativeElement.querySelector);
  • Names of the @Inputs and @Outputs; and
  • Collaborating services (injected via the DI system).

Anything that involves directly invoking methods on the instance or spying on parts of the component is too closely coupled to the implementation, and will add friction to refactoring - test doubles should only be used for the collaborators. In this case, as we have no collaborators, we shouldn't need any mocks, spies or other test doubles.


One way to test this is by subscribing directly to the emitter, then invoking the click action (see Component with inputs and outputs):

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

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

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

  it('should emit when clicked', () => {
    let emitted: string;
    component.clicked.subscribe((event: string) => {
      emitted = event;
    });

    fixture.nativeElement.querySelector('button').click();

    expect(emitted).toBe('clicked!');
  });
});

Although this interacts directly with the component instance, the name of the @Output is part of the public API, so it's not too tightly coupled.


Alternatively, you can create a simple test host (see Component inside a test host) and actually mount your component:

@Component({
  selector: 'test-host',
  template: `
    <my-demo (clicked)="onClicked($event)"></my-demo>
  `
})
class TestHostComponent {
  lastClick = '';

  onClicked(value: string): void {
    this.lastClick = value;
  }
}

then test the component in context:

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TestHostComponent, DemoComponent ]
    })
    .compileComponents();
  }));

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

  it('should emit when clicked', () => {
    fixture.nativeElement.querySelector('button').click();

    expect(component.lastClick).toBe('clicked!');
  });
});

The componentInstance here is the test host, so we can be confident we're not overly coupled to the component we're actually testing.


You could use a spy, depends on your style. Here's how you would use a spy easily to see if emit is being fired off...

it('should emit on click', () => {
    spyOn(component.eventEmitter, 'emit');
    component.buttonClick();
    expect(component.eventEmitter.emit).toHaveBeenCalled();
    expect(component.eventEmitter.emit).toHaveBeenCalledWith('bar');
});

Your test could be:

it('should emit on click', () => {
   const fixture = TestBed.createComponent(MyComponent);
   // spy on event emitter
   const component = fixture.componentInstance; 
   spyOn(component.myEventEmitter, 'emit');

   // trigger the click
   const nativeElement = fixture.nativeElement;
   const button = nativeElement.querySelector('button');
   button.dispatchEvent(new Event('click'));

   fixture.detectChanges();

   expect(component.myEventEmitter.emit).toHaveBeenCalledWith('hello');
});

when your component is:

@Component({ ... })
class MyComponent {
  @Output myEventEmitter = new EventEmitter<string>();

  buttonClick() {
    this.myEventEmitter.emit('hello');
  }
}

You can subscribe to the emitter or bind to it, if it is an @Output(), in the parent template and check in the parent component if the binding was updated. You can also dispatch a click event and then the subscription should fire.