¿Cómo cambiar la implementación simulada por prueba única?
Me gustaría cambiar la implementación de una dependencia simulada en cada prueba ampliando el comportamiento predeterminado del simulacro y revirtiéndolo a la implementación original cuando se ejecute la siguiente prueba.
Más brevemente, esto es lo que estoy tratando de lograr:
- Dependencia simulada
- Cambiar/ampliar la implementación simulada en una sola prueba
- Volver al simulacro original cuando se ejecute la próxima prueba
Actualmente estoy usando Jest v21. Así es como se vería una prueba típica:
// __mocks__/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
// __tests__/myTest.js
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
Esto es lo que he probado hasta ahora:
mockFn.mockImplementationOnce(fn)
it('should override myModule.b mock result (and leave the other methods untouched)', () => { myMockedModule.b.mockImplementationOnce(() => 'overridden'); myModule.a(); // === true myModule.b(); // === 'overridden' });
Ventajas
- Vuelve a la implementación original después de la primera llamada.
Contras
- Se rompe si la prueba llama
b
varias veces - No vuelve a la implementación original hasta que
b
no se llama (se filtrará en la próxima prueba)
jest.doMock(moduleName, factory, options)
it('should override myModule.b mock result (and leave the other methods untouched)', () => { jest.doMock('../myModule', () => { return { a: jest.fn(() => true, b: jest.fn(() => 'overridden', } }); myModule.a(); // === true myModule.b(); // === 'overridden' });
Ventajas
- Se vuelve a burlar explícitamente en cada prueba.
Contras
- No se puede definir una implementación simulada predeterminada para todas las pruebas
- No se puede extender la implementación predeterminada, lo que obliga a volver a declarar cada método simulado
Burlarse manualmente con métodos de establecimiento (como se explica aquí )
// __mocks__/myModule.js const myMockedModule = jest.genMockFromModule('../myModule'); let a = true; let b = true; myMockedModule.a = jest.fn(() => a); myMockedModule.b = jest.fn(() => b); myMockedModule.__setA = (value) => { a = value }; myMockedModule.__setB = (value) => { b = value }; myMockedModule.__reset = () => { a = true; b = true; }; export default myMockedModule;
// __tests__/myTest.js it('should override myModule.b mock result (and leave the other methods untouched)', () => { myModule.__setB('overridden'); myModule.a(); // === true myModule.b(); // === 'overridden' myModule.__reset(); });
Ventajas
- Control total sobre los resultados simulados
Contras
- Mucho código repetitivo
- Difícil de mantener a largo plazo
jest.spyOn(object, methodName)
beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); // Mock myModule jest.mock('../myModule'); it('should override myModule.b mock result (and leave the other methods untouched)', () => { const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden'); myMockedModule.a(); // === true myMockedModule.b(); // === 'overridden' // How to get back to original mocked value? });
Contras
- No puedo volver
mockImplementation
al valor de retorno simulado original, lo que afecta las próximas pruebas.
- No puedo volver
Utilice mockFn.mockImplementation(fn) .
import { funcToMock } from './somewhere';
jest.mock('./somewhere');
beforeEach(() => {
funcToMock.mockImplementation(() => { /* default implementation */ });
// (funcToMock as jest.Mock)... in TS
});
test('case that needs a different implementation of funcToMock', () => {
funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
// (funcToMock as jest.Mock)... in TS
// ...
});
Un buen patrón para escribir pruebas es crear una función de configuración de fábrica que devuelva los datos necesarios para probar el módulo actual.
A continuación se muestra un código de muestra que sigue el segundo ejemplo, aunque permite proporcionar valores predeterminados y anulados de forma reutilizable.
const spyReturns = returnValue => jest.fn(() => returnValue);
describe("scenario", () => {
beforeEach(() => {
jest.resetModules();
});
const setup = (mockOverrides) => {
const mockedFunctions = {
a: spyReturns(true),
b: spyReturns(true),
...mockOverrides
}
jest.doMock('../myModule', () => mockedFunctions)
return {
mockedModule: require('../myModule')
}
}
it("should return true for module a", () => {
const { mockedModule } = setup();
expect(mockedModule.a()).toEqual(true)
});
it("should return override for module a", () => {
const EXPECTED_VALUE = "override"
const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)});
expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
});
});
Es importante decir que debes restablecer los módulos que se han almacenado en caché usando jest.resetModules()
. Esto se puede hacer en beforeEach
una función de desmontaje similar.
Consulte la documentación del objeto broma para obtener más información: https://jestjs.io/docs/jest-object .
Un poco tarde para la fiesta, pero si alguien más tiene problemas con esto.
Usamos TypeScript, ES6 y babel para el desarrollo nativo de reacción.
Por lo general, nos burlamos de los módulos NPM externos en el __mocks__
directorio raíz.
Quería anular una función específica de un módulo en la clase Auth de aws-amplify para una prueba específica.
import { Auth } from 'aws-amplify';
import GetJwtToken from './GetJwtToken';
...
it('When idToken should return "123"', async () => {
const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
getIdToken: () => ({
getJwtToken: () => '123',
}),
}));
const result = await GetJwtToken();
expect(result).toBe('123');
spy.mockRestore();
});
Esencial: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2
Tutorial: https://medium.com/p/b4ac52a005d#19c5