Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Jest, how can I unit test a method that subscribes to an observable

I have various methods in my components that subscribe to methods in injected dependencies, that return observables.

I want to write Jest unit tests to ensure that when these observables return / error, my methods do the correct thing.

In the below example I am trying to write a test that checks if doAThing has fired. Neither of the below tests work. They both fail with errors like

'returnMyObservable.subscribe is not a function'.

// Example method to test component 
public testFunction (): void {
    this.myService.returnMyObservable.subscribe(
        ( value ) => this.doAThing( value )
    )
}
describe( 'myComponemt', () => {

    let fixture;
    let myServiceMock;

    beforeEach( () => {
        myServiceMock = {
            returnMyObservable: fn()
        }

        fixture = new myComponent( myServiceMock );
    });


    // 1) I have tried mocking with a returned value
    it ( 'should call do a thing when value is returned', () => {
        myServiceMock.returnMyOnservable.mockReturnValue( true );

        fixture.testFunction();

        expect( fixture.doAThing ).toHaveBeenCalled();
    });

    // 2) I have tried returning an observable
    it ( 'should call do a thing when value is returned', () => {
        myServiceMock.returnMyOnservable.mockReturnValue( of( true ) );

        fixture.testFunction();

        expect( fixture.doAThing ).toHaveBeenCalled();
    });

});
like image 307
TheRyanSmee Avatar asked Nov 12 '19 09:11

TheRyanSmee


People also ask

What is jest spyOn?

Jest's spyOn method is used to spy on a method call on an object. It is also very beneficial in cases where the Jest mock module or mock function might not be the best tool for the job on hand. While writing unit tests you only test one particular unit of code, generally a function.

How do you mock a function?

There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.

What is describe method in jest?

describe breaks your test suite into components. Depending on your test strategy, you might have a describe for each function in your class, each module of your plugin, or each user-facing piece of functionality. You can also nest describes to further subdivide the suite. it is where you perform individual tests.


2 Answers

I had a few other errors else where that were masking what was actually wrong with my tests - I have found that the best way to test the above functionality is with:

describe( 'MyComponent', () => {
    let fixture;
    let myServiceMock;

    beforeEach( () => {
        myServiceMock = {
            returnMyObservable: jest.fn()
        }

        fixture = new MyComponent( myServiceMock );
    });

    it ( 'should call doAThing when value is returned', () => {
        const doAThingSpy = jest.spyOn( fixture, 'doAThing' );
        myServiceMock.returnMyObservable.mockReturnValue( of( true ) );

        fixture.testFunction();

        expect( doAThingSpy ).toHaveBeenCalledWith( true );
    });
});

(This is pretty much the same way to do it in Jasmine too)

like image 73
TheRyanSmee Avatar answered Oct 10 '22 20:10

TheRyanSmee


We can use mockImplementation to mock the implementation of this.myService.returnMyObservable. After that, we can get the function you passed in subscribe in the test case(observer), then execute it manually.

Here is the solution:

index.ts:

export class MyComponent {
  private myService;
  constructor(myService) {
    this.myService = myService;
  }
  public testFunction(): void {
    this.myService.returnMyObservable.subscribe(value => this.doAThing(value));
  }
  public doAThing(value) {}
}

index.spec.ts:

import { MyComponent } from './';

describe('MyComponent', () => {
  let fixture;
  let myServiceMock;

  beforeEach(() => {
    myServiceMock = {
      returnMyObservable: {
        subscribe: jest.fn()
      }
    };
    fixture = new MyComponent(myServiceMock);
  });

  it('should call do a thing when value is returned', () => {
    let observer;
    myServiceMock.returnMyObservable.subscribe.mockImplementation(handler => {
      observer = handler;
    });
    jest.spyOn(fixture, 'doAThing');
    fixture.testFunction();
    observer();

    expect(fixture.doAThing).toHaveBeenCalled();
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/58815471/index.spec.ts (7.367s)
  MyComponent
    ✓ should call do a thing when value is returned (5ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.773s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58815471

like image 36
slideshowp2 Avatar answered Oct 10 '22 19:10

slideshowp2