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();
});
});
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.
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.
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.
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With