javascript - attribute - Cómo cambiar la implementación simulada en base a una sola prueba
title html css (3)
Poco tarde a la fiesta, pero si alguien más está teniendo problemas con esto.
Utilizamos TypeScript, ES6 y babel para el desarrollo nativo reactivo.
Por lo general, __mocks__
módulos NPM externos en el directorio raíz __mocks__
.
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();
});
Gist: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2
Tutorial: https://medium.com/p/b4ac52a005d#19c5
Me gustaría cambiar la implementación de una dependencia simulada en base a una sola prueba extendiendo el comportamiento de la simulación predeterminada y volviendo a la implementación original cuando se ejecuta la siguiente prueba.
Más brevemente esto es lo que estoy tratando de lograr:
- simulacro de dependencia
- Cambiar / extender la implementación simulada en una sola prueba.
- volver a la simulación original cuando se ejecuta la siguiente prueba
Actualmente estoy usando Jest v21
.
Aquí es cómo se vería una típica prueba de Jest:
__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 eeffects
});
it(''should revert back to default myMockedModule mock'', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
Probé algunas estrategias, pero no encontré ninguna solución que pudiera definir satisfactoria.
1 - mockFn.mockImplementationOnce(fn)
pros
- Vuelve a la implementación original después de la primera llamada.
contras
- Se romperá si la prueba llama
b
más veces - No volverá a la implementación original hasta que no se llame a
b
(se filtra en la siguiente prueba)
código:
it(''should override myModule.b mock result (and leave the other methods untouched)'', () => {
myMockedModule.b.mockImplementationOnce(() => ''overridden'');
myModule.a(); // === true
myModule.b(); // === ''overridden''
});
2 - jest.doMock (nombre de módulo, fábrica, opciones)
pros
- Se vuelve a imitar explícitamente en cada prueba
contras
- No se puede definir una implementación simulada predeterminada para todas las pruebas
- No se puede extender una implementación predeterminada forzando a volver a declarar cada método simulado
código:
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''
});
3 - Simulación manual con métodos de establecimiento (como se explica here )
pros
- Control total sobre resultados burlados.
contras
- Lote de código repetitivo
- Difícil de mantener a largo plazo
código:
__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();
});
4 - jest.spyOn (object, methodName)
contras
- No puedo revertir
mockImplementation
al valor de retornomockImplementation
original, por lo tanto afectando las siguientes pruebas
código:
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 the original mocked value?
});
Gracias de antemano por cualquier entrada / sugerencia!
Un buen patrón para la prueba de escritura es crear una función de configuración de fábrica que devuelva los datos que necesita para probar el módulo actual.
A continuación se muestra un código de ejemplo que sigue a su segundo ejemplo, aunque permite la provisión de valores predeterminados y de anulación de manera reutilizable.
const spyReturns = returnValue => jest.fn(() => returnValue);
describe("scenario", () => {
const setup = (mockOverrides) => {
const mockedFunctions = {
a: spyReturns(true),
b: spyReturns(true),
...mockOverrides
}
return {
mockedModule: jest.doMock(''../myModule'', () => mockedFunctions);
}
}
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)
})
});
Utilice mockFn.mockImplementation(fn) .
Ponga la implementación predeterminada en el beforeEach
. El simulacro se reiniciará antes de cada prueba.
Para anular, use mockImplementation
en la prueba.
Esto anulará el comportamiento del simulacro para cualquiera / todas las llamadas en la prueba, y será sobrescrito por la implementación de beforeEach
antes de la siguiente prueba.
Por ejemplo:
import { funcToMock } from ''./somewhere'';
jest.mock(''./somewhere'');
beforeEach(() => {
funcToMock.mockImplementation(() => { /* default implementation */ });
});
test(''case that needs a different implementation of funcToMock'', () => {
funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
// ...
});