Search code examples
angularunit-testingangular7karma-jasmine

how to trigger a subscribe to an event emitter inside a function. Angular unit test


I've been trying to test a function that contains a subscribe of an EventEmitter, the unit test is calling the subscribe but It has been impossible to test the code inside the subscribe, I mean, the filter and getDocuments() are not being tested even though the subscribe is being called.

  describe('trigger_data test', () => {
    it('not filter in storage', async(() => {
      component.filter = null;
      spyOn(component, 'getDocuments');
      spyOn(mockSharedService.trigger_data, 'subscribe').and.returnValue(of(true));
      component.triggerData();
      expect(mockSharedService.trigger_data.subscribe).toHaveBeenCalled();
      expect(component.filter).toEqual(1); //Error: Expected null to equal 1.
      expect(component.getDocuments).toHaveBeenCalled(); //Error: Expected spy getDocuments to have been called.
    }));
  });

This is the function

  triggerData() {
    this.sharedService.trigger_data.subscribe(() => {
      if (sessionStorage.getItem('filter_pp') !== null) {
        this.filter = Number(sessionStorage.getItem('filter_pp'));
      } else {
        this.filter = 1;
      }
      this.getDocuments();
    });
  }

When another component emits a boolean the subscribe trigger the code.


Solution

  • You are spying on the subscribe method and having it return an Observable, which is not correct. (subscribe is a method on an Observable that returns a Subscription)

    There are a few ways you can go about fixing this:

    • Ensure mockSharedService.trigger_data is set to the correct Observable when creating the mock (For that, I think we need to see more of your testing code).
    • Override the mockSharedService.trigger_data in your test using: mockSharedService.trigger_data = of(true) (Note: if trigger_data is an EventEmitter, TypeScript is not going to like this and you might need: mockSharedService.trigger_data = of(true) as any)
    • If you want to avoid casting to any, as I've done in the previous example, you can use:
      const emitter = new EventEmitter();
      mockSharedService.trigger_data = emitter;
      emitter.emit(true);
    
    • Make trigger_data a method that returns an Observable and spyOn that method using spyOn(mockSharedService, 'trigger_data').and.returnValue(of(true)). However, doing so will also require you to change your triggerData implementation to call trigger_data() instead of just accessing it as a property.

    I've been trying to test a function that contains a subscribe of an EventEmitter

    Additional, you probably want to use a Subject instead of an EventEmitter, EventEmitters are used by Angular for their Output bindings. When implementing ourselves, outside of Angular's Outputs we can make use of Subjects instead of EventEmitters (which actually extends Subjects, for the purpose of Angular's Outputs. See: https://github.com/angular/angular/blob/master/packages/core/src/event_emitter.ts#L64)