Tenho o seguinte módulo que estou tentando testar no Jest:
// myModule.js
export function otherFn() {
console.log('do something');
}
export function testFn() {
otherFn();
// do other things
}
Conforme mostrado acima, ele exporta algumas funções nomeadas e testFn
usos importantes otherFn
.
De brincadeira, quando estou escrevendo meu teste de unidade para testFn
, quero simular a otherFn
função porque não quero que erros no otherFn
afetem meu teste de unidade para testFn
. Meu problema é que não tenho certeza da melhor maneira de fazer isso:
// myModule.test.js
jest.unmock('myModule');
import { testFn, otherFn } from 'myModule';
describe('test category', () => {
it('tests something about testFn', () => {
// I want to mock "otherFn" here but can't reassign
// a.k.a. can't do otherFn = jest.fn()
});
});
Qualquer ajuda / visão é apreciada.
unit-testing
ecmascript-6
jestjs
Jon Rubins
fonte
fonte
otherFn
em um módulo separado e simular isso.function A
quem chama,function B
mas não quero executar a implementação real defunction B
porque quero apenas testar a lógica implementada emfunction A
@testing-library/react
para qualquer Reacters por aí), mas sei que este é um assunto controverso.Respostas:
Use
jest.requireActual()
dentrojest.mock()
Exemplo
Prefiro esse uso conciso, onde você exige e espalha dentro do objeto retornado:
// myModule.test.js jest.mock('./myModule.js', () => ( { ...(jest.requireActual('./myModule.js')), otherFn: jest.fn() } )) import { otherFn } from './myModule.js' describe('test category', () => { it('tests something about otherFn', () => { otherFn.mockReturnValue('foo') expect(otherFn()).toBe('foo') }) })
Este método também é referenciado na documentação do Manual Mocks de Jest (próximo ao final dos Exemplos ):
fonte
return
instrução e envolvendo o corpo da função de seta entre parênteses: por exemplo.jest.mock('./myModule', () => ({ ...jest.requireActual('./myModule'), otherFn: () => {}}))
...jest.requireActual
não funcionou corretamente para mim porque eu tenho o aliasing de caminho usando o babel. Funciona com...require.requireActual
ou depois de remover o aliasing do caminhootherFun
foi chamado neste caso? AssumindootherFn: jest.fn()
mockReturnValue
método para demonstrar melhor que a versão simulada está sendo chamada em vez da original, mas se você realmente quiser apenas ver se ela foi chamada sem declarar contra o valor de retorno, você pode usar o matcher jest.toHaveBeenCalled()
.import m from '../myModule';
Não funciona para mim, eu usei:
import * as m from '../myModule'; m.otherFn = jest.fn();
fonte
clearkMocks: true
jest package.json. facebook.github.io/jest/docs/en/mock-function-api.htmlParece que estou atrasado para esta festa, mas sim, isso é possível.
testFn
só precisa chamarotherFn
usando o módulo .Se
testFn
usar o módulo para chamarotherFn
, a exportação do módulo paraotherFn
pode sertestFn
simulada e chamará a simulação.Aqui está um exemplo prático:
myModule.js
import * as myModule from './myModule'; // import myModule into itself export function otherFn() { return 'original value'; } export function testFn() { const result = myModule.otherFn(); // call otherFn using the module // do other things return result; }
myModule.test.js
import * as myModule from './myModule'; describe('test category', () => { it('tests something about testFn', () => { const mock = jest.spyOn(myModule, 'otherFn'); // spy on otherFn mock.mockReturnValue('mocked value'); // mock the return value expect(myModule.testFn()).toBe('mocked value'); // SUCCESS mock.mockRestore(); // restore otherFn }); });
fonte
exports.otherFn()
exports
não existe no ES6. A chamadaexports.otherFn()
funciona agora porque o ES6 está sendo compilado com uma sintaxe de módulo anterior, mas será interrompido quando o ES6 for suportado nativamente.O código transpilado não permitirá que o babel recupere o vínculo ao qual
otherFn()
está se referindo. Se você usar uma expessão de função, deverá conseguir realizar a simulaçãootherFn()
.// myModule.js exports.otherFn = () => { console.log('do something'); } exports.testFn = () => { exports.otherFn(); // do other things }
// myModule.test.js import m from '../myModule'; m.otherFn = jest.fn();
Mas, como @kentcdodds mencionou no comentário anterior, você provavelmente não gostaria de zombar
otherFn()
. Em vez disso, basta escrever uma nova especificaçãootherFn()
e simular todas as chamadas necessárias que ele está fazendo.Por exemplo, se
otherFn()
estiver fazendo uma solicitação http ...// myModule.js exports.otherFn = () => { http.get('http://some-api.com', (res) => { // handle stuff }); };
Aqui, você deseja simular
http.get
e atualizar suas asserções com base em suas implementações simuladas .// myModule.test.js jest.mock('http', () => ({ get: jest.fn(() => { console.log('test'); }), }));
fonte
otherFn
estiver quebrado, irá falhar em todos os testes que dependem daquele. Além disso, seotherFn
tiver 5 ifs dentro, você pode precisar testar setestFn
está funcionando bem para todos os subcasos. Você terá muitos mais caminhos de código para testar agora.Sei que isso foi perguntado há muito tempo, mas me deparei com essa mesma situação e finalmente encontrei uma solução que funcionaria. Então eu pensei em compartilhar aqui.
Para o módulo:
// myModule.js export function otherFn() { console.log('do something'); } export function testFn() { otherFn(); // do other things }
Você pode alterar o seguinte:
// myModule.js export const otherFn = () => { console.log('do something'); } export const testFn = () => { otherFn(); // do other things }
exportá-los como constantes em vez de funções. Eu acredito que o problema tem a ver com o içamento em JavaScript e o uso de
const
prevenir esse comportamento.Então, em seu teste, você pode ter algo como o seguinte:
import * as myModule from 'myModule'; describe('...', () => { jest.spyOn(myModule, 'otherFn').mockReturnValue('what ever you want to return'); // or myModule.otherFn = jest.fn(() => { // your mock implementation }); });
Seus mocks agora devem funcionar como você normalmente esperaria.
fonte
Resolvi meu problema com uma combinação das respostas que encontrei aqui:
myModule.js
import * as myModule from './myModule'; // import myModule into itself export function otherFn() { return 'original value'; } export function testFn() { const result = myModule.otherFn(); // call otherFn using the module // do other things return result; }
myModule.test.js
import * as myModule from './myModule'; describe('test category', () => { let otherFnOrig; beforeAll(() => { otherFnOrig = myModule.otherFn; myModule.otherFn = jest.fn(); }); afterAll(() => { myModule.otherFn = otherFnOrig; }); it('tests something about testFn', () => { // using mock to make the tests }); });
fonte
Além da primeira resposta aqui, você pode usar o babel-plugin-rewire para simular a função nomeada importada também. Você pode verificar a seção superficialmente para religamento de funções nomeadas .
Um dos benefícios imediatos para sua situação aqui é que você não precisa alterar como você chama a outra função de sua função.
fonte