typescript - En Jest, ¿cómo puedo hacer una prueba unitaria de un método que se suscribe a un observable?
Tengo varios métodos en mis componentes que se suscriben a métodos en dependencias inyectadas, que devuelven observables.
Quiero escribir pruebas unitarias con Jest para asegurarme de que cuando estos observables devuelvan un resultado o un error, mis métodos hagan lo correcto.
En el siguiente ejemplo estoy tratando de escribir una prueba que verifique si doAThing se ha ejecutado. Ninguna de las pruebas siguientes funciona. Ambas fallan con errores como
'returnMyObservable.subscribe no es una función'.
// 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();
});
});
Respuesta #1
Tenía algunos otros errores en otros lugares que estaban enmascarando lo que realmente estaba mal con mis pruebas; he descubierto que la mejor manera de probar la funcionalidad anterior es con:
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 );
});
});
(Esto es prácticamente la misma manera de hacerlo en Jasmine también)
Respuesta #2
Podemos usar mockImplementation para simular la implementación de this.myService.returnMyObservable. Después de eso, podemos obtener la función que pasaste en subscribe en el caso de prueba (observer), y luego ejecutarla manualmente.
Aquí está la solución:
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();
});
});
Resultado de la prueba unitaria con 100% de cobertura:
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
Código fuente: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58815471
