Eu gostaria de alterar a implementação de uma dependência zombado em um por base um único teste por estender a simulação padrão de comportamento e revertendo-lo de volta para a implementação original quando os próximos executa testes.
Mais resumidamente, isso é o que estou tentando alcançar:
- dependência simulada
- alterar / estender a implementação simulada em um único teste
- reverter para a simulação original quando o próximo teste for executado
Atualmente estou usando Jest v21
.
Aqui está a aparência de um teste de Jest típico:
__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
});
});
Aqui está o que tentei até agora:
1 - mockFn.mockImplementationOnce (fn)
prós
- Reverte para a implementação original após a primeira chamada
contras
- Ele quebra se o teste chamar
b
várias vezes - Ele não reverte para a implementação original até que
b
não seja chamado (vazando no próximo teste)
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 (moduleName, factory, options)
prós
- Mocks explicitamente em cada teste
contras
- Não é possível definir a implementação simulada padrão para todos os testes
- Não é possível estender a implementação padrão, forçando a declarar novamente 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 - Simulação manual com métodos setter (conforme explicado aqui )
prós
- Controle total sobre resultados simulados
contras
- Lote de código clichê
- Difícil de manter a longo prazo
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 (objeto, methodName)
contras
- Não consigo reverter
mockImplementation
para o valor de retorno simulado original, afetando os próximos testes
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 original mocked value?
});
javascript
unit-testing
mocking
jestjs
Andrea Carraro
fonte
fonte
Respostas:
Um bom padrão para escrever teste é criar uma função de configuração de fábrica que retorna os dados necessários para testar o módulo atual.
Abaixo está algum código de amostra após seu segundo exemplo, embora permita o fornecimento de valores padrão e de substituição de uma forma reutilizável.
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) }); });
fonte
Vanilla JS
Use mockFn.mockImplementation (fn) .
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 */ }); // ... });
TypeScript
Para evitar que a mensagem mockImplementation não seja uma propriedade de funcToMock , você precisará especificar o tipo, por exemplo, alterando a linha superior acima para a seguinte:
import { (funcToMock as jest.Mock) } from './somewhere';
Uma questão que aborda esse problema pode ser encontrada aqui: jest typecript propriedade mock não existe no tipo
fonte
Um pouco tarde para a festa, mas se alguém estiver tendo problemas com isso.
Usamos TypeScript, ES6 e babel para desenvolvimento reagente nativo.
Normalmente simulamos módulos NPM externos no
__mocks__
diretório raiz .Eu queria substituir uma função específica de um módulo na classe Auth do aws-amplify para um teste específico.
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(); });
Síntese: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2
Tutorial: https://medium.com/p/b4ac52a005d#19c5
fonte