<p>jest.mock factory não funciona dentro de um teste</p>

Criado em 12 jan. 2017  ·  38Comentários  ·  Fonte: facebook/jest

Você quer solicitar um recurso ou relatar um bug ?
Erro

Qual é o comportamento atual?

Parece que a maneira de criar um mock com uma fábrica não funciona dentro de test ou it . Funciona apenas quando o mock é definido no nível raiz do arquivo.

Aqui está meu exemplo de simulação:

jest.mock('services/feature', () => ({
    isEnabled: () => true
}));

Qual é o comportamento esperado?

Zombar de um arquivo dentro de um teste deve funcionar.

Forneça sua configuração exata do Jest e mencione seu Jest, nó, versão do yarn / npm e sistema operacional.

Jest 18.0.0, Node 7.4, macOS

Confirmed Discussion

Comentários muito úteis

Para alterar o valor de retorno de uma simulação entre os testes, você pode fazer algo assim:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

Todos 38 comentários

jest.mock chamadas são automaticamente içadas para o topo do arquivo com a transformação babel-jest. Você pode omitir esse comportamento com jest.doMock . Você já tentou isso?

Mesma coisa com doMock .

Na documentação, posso ler

Nota: Ao usar babel-jest , as chamadas para simulação serão automaticamente içadas para o topo do bloco de código. Use doMock se quiser evitar explicitamente esse comportamento.

Mas ... Um teste é um bloco de código, certo? Portanto, no meu caso, não espero ver nenhuma diferença.

Aqui está um teste completo

it('renders with the enabled feature', () => {
  jest.mock('services/feature', () => ({
      isEnabled: () => true
  }));

  const component = renderer.create(
      <MyComponent />
  );

  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

Você poderia fornecer uma reprodução disso em um repositório GH?

Certo. Aqui está: https://github.com/tleunen/jest-issue-2582

Ambos os testes são renderizados como "Desativado", embora um deles tenha uma simulação para renderizar como "Ativado".
Veja estes:
https://github.com/tleunen/jest-issue-2582/blob/master/src/MyComponent.js
https://github.com/tleunen/jest-issue-2582/blob/master/src/__tests__/MyComponent.spec.js

Obrigado.

Algum conselho @thymikee @cpojer para esse problema?
Tenho vários testes no mesmo arquivo e gostaria de ter diferentes respostas de simulação para cada um deles.

Ainda bem que encontrei esse problema, estava quebrando a cabeça por que jest.mock() não funcionou no meu escopo describe . Mudei para o topo (abaixo das minhas importações no arquivo de teste) e funciona.

Para mim, também se aplica a jest.mock() sem fábrica, usando uma pasta __mocks__ contendo o arquivo simulado.

--editar

Obviamente, é necessário içar a instrução jest.mock() antes das instruções de importação. No entanto, @tleunen , isso provavelmente significa que não é possível simular o mesmo arquivo mais de uma vez, com respostas diferentes. Talvez seja melhor tornar a função de fábrica mais dinâmica, para que ela possa fornecer resultados diferentes em cada caso de teste.

Em minha mente, ficaria mais explícito se jest.mock() fosse sempre colocado fora dos blocos describe e it . Mas isso deve ser claramente declarado nos documentos

Então jest.mock está sendo içado para o escopo da função, é por isso que não funcionará com require s (e definitivamente não import s que são içados para o escopo do módulo) se você chamá-lo dentro de uma função diferente de describe (que é tratada especialmente por Jasmine).
Sua chamada jest.mock será içada para o topo dessa mesma função (não o módulo), é por isso que não funcionará da maneira que você espera.

Geralmente, aconselhamos configurar simulações diferentes em beforeEach e afterEach se você quiser que sejam diferentes nos casos de teste.

@cpojer poderia elaborar isso em detalhes, e se quisermos içar as chamadas para os escopos superiores.

Isso ocorre porque você está exigindo seus módulos quando o módulo é inicializado (usando importação). jest.mock é chamado mais tarde. A maneira de resolver isso é:

beforeEach(() => { // or the specific test
  jest.mock('MyModule', () => …);
  const MyModule = require('MyModule');
  …
});

etc.

Se você fizesse isso em beforeEach, não sei como você diferenciaria os testes (então, como você estaria dando um mock diferente para cada teste?)

Colocá-lo dentro dos próprios testes funciona, é claro.

Você pode então passar o módulo simulado dentro do componente testado?

E se eu não estiver importando / exigindo o arquivo que desejo simular (por exemplo, uma dependência de outro arquivo que estou importando), mas quiser que ele seja definido como um bloco de descrição / it? Ou mesmo se eu quiser simular de forma diferente para o teste beforeEach / beforeAll? Esses casos são possíveis?

// A.js depends on B.js
import A from './A';

describe('myTest', () => {

    describe('myFirstScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                myFirstMethod: jest.fn(),
            }));
        });

        // tests here
    });

    describe('mySecondScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                mySecondMethod: jest.fn(),
            }));
        });

        // tests here
    });
});

Nesse caso, você precisa exigir A depois de simular B. (não usando import , mas require ).

exigindo A depois de simular B

não funcionou para mim, ainda estava vendo a simulação original de B passar para o resultado

@GoldAnna , você encontrou uma solução alternativa para esse problema?

@alayor eu não tenho

Por mais vezes que já tenha encontrado esse problema, estou convencido de que não estou testando corretamente, ou não estou usando jest como os autores pretendiam, ou alguma combinação dos dois. Outra prova é que quase nenhum dos exemplos de simulação relacionados na documentação de jest parecem exemplos do mundo real para mim ... provavelmente são e minha abordagem está provavelmente incorreta.

Dito isso, o seguinte funciona para mim e todos os testes a seguir são aprovados. Observe que para voltar à versão original de ModuleB , eu tenho que chamar jest.resetModules() e jest.unmock('./moduleB') ... a ordem deles não importa.

// Module A
const ModuleB = require('./moduleB');
const ModuleA = function() {
  this.title = new ModuleB().title;
  return this;
};
module.exports = ModuleA;

// Module B
const ModuleB = function() {
  this.title = 'Module B - Original'
  return this;
};
module.exports = ModuleB;

// Tests
describe('Jest a few tests', () => {
  it('should do something', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 1'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 1');
  });

  it('should do something else', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 2'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 2');
  });

  it('should do something original', () => {
    jest.resetModules();
    jest.unmock('./moduleB');
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Original');
  });
});

Ei,

Nada aqui funciona para mim.
Alguém pode explicar por que não é possível usar jest.mock dentro dele?

Obrigado

Acho que ainda é um problema que talvez deva ser reaberto como uma solicitação de recurso. Quero escrever testes isoladamente. Despejar coisas em uma pasta de simulação ou religar com um beforeEach geralmente termina com uma gaveta de lixo de simulação / instanciação de dados estranhos que é arrastada para todos os testes. Eu quero escrever um pequeno mock e fazer um pequeno teste passar.

Não temos uma maneira real de isolar testes individuais (considerando test.concurrent ). Se adotássemos uma API semelhante a tap ou ava seria possível, mas não acho que seja possível com as restrições impostas pela API global atual. Super feliz por estar errado!

Tive uma luta com vários métodos aqui para refatorar um teste previamente escrito em mocha e proxyquire, acabando com a separação do teste em arquivos diferentes para diferentes simulações.

@gcox Você já tentou fazer o mockImplementation para fazer isso. É assim que resolvemos isso em nossas suítes de teste.

@antgonzales Acho que isso é difícil de fazer se as importações de módulo forem içadas para o topo de um módulo. Modificar as referências de um módulo importado anteriormente parece não trivial ou impossível no nó.

@jkomusin Com certeza. Isso simplesmente não era o que o OP estava pedindo, IMO. Acredito que eles estavam perguntando, especificamente, "Como faço para forçar require('whatever') a retornar um mock diferente em vários testes adjacentes em vez do mesmo objeto?".

Para alterar o valor de retorno de uma simulação entre os testes, você pode fazer algo assim:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

A resposta @SimenB também funcionou e é muito mais simples!

@rafaeleyng mas @SimenB não funciona se o que você exporta do módulo não é uma função ...

O exemplo

@schumannd está certo. Por favor, torne a documentação oficial mais clara e nítida.

Os PRs são sempre bem-vindos para melhorar a documentação 🙂

Exemplo de módulo baseado em
https://github.com/facebook/jest/issues/2582#issuecomment -378677440 ❤️

Ei, descobri isso por meio do problema vinculado e descobri:

jest.mock('child_process')
const childProcess = require('child_process')

describe('foo', () => {
  test('bar', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wobble')
  })
})

Se você precisar simular várias funções / métodos no código necessário, será necessário configurá-los separadamente em vez de usar a fábrica ao fazer isso globalmente.

Se você gosta de mim, só precisa de uma única função, pode simplificar tudo:

jest.mock('child_process')
const { execSync } = require('child_process')

describe('foo', () => {
  test('bar', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wobble')
  })
})

que tal simular dependências não funcionais como arquivos json ou constantes mapeadas. Por favor, deixe a documentação mais clara sobre como lidar com isso ...

@gcox Sua solução é a única que encontrei para quando um módulo importado em teste importa outro módulo para o qual eu tinha uma simulação manual em uma pasta __mocks__ .

Por exemplo, o arquivo de teste chama jest.mock('./ModuleA') , que tem uma simulação em __mocks__/ModuleA.js . Mas o Módulo A não está sendo testado, o Módulo B - que requer o Módulo A - é o que está sendo testado. Sem sua solução, o Módulo B obteria a implementação real do Módulo A, não a simulação.

Isso parece um comportamento muito estranho de brincadeira. Eu esperaria que, ao chamar jest.mock em um módulo, qualquer módulo em teste que tenha uma dependência do módulo simulado usaria o simulado. Parece bizarro que não funcione dessa maneira ou estou fazendo isso de forma completamente incorreta?

@SimenB Em seu exemplo, você está exigindo o módulo simulado e executando-o no teste, o que funciona. Você espera que o cenário que descrevi acima funcione bem? Muito obrigado.

@SimenB trabalhou sua solução mais simples para mim. Obrigado! Wish poderia encontrá-lo mais cedo, antes que eu passasse um tempo com muitas maneiras diferentes

No meu caso, eu estava zombando e importando o caminho errado para o módulo que queria criar um stub. Eu tinha diferentes maiúsculas e minúsculas no caminho do arquivo, então meu editor achou que estava tudo bem, mas estava falhando.

Era

const { stubMethod } = require("./path/to/File");
jest.mock("./path/to/File");

Alterado para (alterar a caixa de File para file ):

const { stubMethod } = require("./path/to/file");
jest.mock("./path/to/file");

Espero que isso ajude mais alguém.

Jasmine spyOn() não tem esse problema em testes internos. Jest supostamente usa jasmim por padrão? Por que não funciona dentro do teste?

Exemplo de Jasmim:

spyOn(require('moduleB'), 'functionA').and.callFake(() => true);

O que eu não entendo muito bem quando se trata de módulos de simulação é que sempre é descrito como se você quisesse usar o módulo principalmente em suas funções de teste. Por exemplo, se você olhar o código de exemplo para a função doMock nos documentos:

test('moduleName 2', () => {
  jest.doMock('../moduleName', () => {
    return {
      __esModule: true,
      default: 'default2',
      foo: 'foo2',
    };
  });
  return import('../moduleName').then(moduleName => {
    expect(moduleName.default).toEqual('default2');
    expect(moduleName.foo).toEqual('foo2');
  });
});

Neste exemplo, você tem acesso à nova versão simulada de "moduleName" dentro da função de teste. Para mim, é muito mais importante que meu código de "não teste" também tenha acesso à versão simulada do módulo. Por exemplo, uma classe que estou usando dentro do meu teste, que importou "moduleName" , ainda estaria usando a versão original, não a simulada.

Parece estar relacionado: https://github.com/facebook/jest/issues/3236

Dica - se você tiver um módulo que exporta um valor primitivo, por exemplo:

`` `auth.js
export const isUserAdmin = getSetting ('admin');

And you want to use a mock value instead, in the test, then a simple require and assignment seems to do the trick:

```deleteUser.js
const auth = require('../auth');

describe('deleteUser', () => {
  it('can delete if admin', () => {
    auth.isUserAdmin = true;

    // call method that depends on isUserAdmin value
    // and assert on the result
  });
});

Então, basicamente, se você quiser simular um objeto que é usado indiretamente pelo código, teste a função jest.doMock() não funcionará:

import myModuleToTest from './myModuleTotest'

describe('Given my module', () => {
  it('property1 will work as expect', () => {
    // Testing parts of the module that don't need to be mocked
  })

  it('property2 will work as expected', () => {
    jest.doMock('./myOtherModule', () => {
      return {
        __esModule: true,
        default: 'default2',
        foo: 'foo2',
      };
    });

    import('./myOtherModule').then(myOtherModule => {
      // I'm not interested on the mocked module myOtherModule but on the module that makes use of it
      myModuleToTest.doSomethingToSomeProperty(); // At this point myOtherModule's original module and not its mocked version will be used by myModuleToTest
      expect(myModuleToTest.someProperty).toBe('thisWillFail'); // The test won't pass because the mocked version wasn't used
    });
  });
});

A partir de Jest 26, não há como simular mais de uma vez um módulo exportando um Object que é usado indiretamente (quero dizer zombar de outra coisa que não um Function pois não há mockFn.mockImplementation(fn) para simulado Objects ). Isso está correto ou estou faltando alguma coisa? A única solução então é ter mais de um arquivo de teste para testar o mesmo módulo.

Você tem que importar myModuleToTest após o seu mock porque se ele importa
antes da simulação não faz sentido. Portanto, não use importar .... no topo ou
dentro do retorno de chamada porque é içado de qualquer maneira.

Em Ter, 7 de julho de 2020, 22h24 Antonio Redondo [email protected]
escreveu:

Então, basicamente, se você quiser simular um objeto que é usado indiretamente pelo
o código que você testar a função jest.doMock () não funcionará:

import myModuleToTest de './myModuleTotest'
it ('funcionará', () => {
jest.doMock ('./ myOtherModule', () => {
Retorna {
__esModule: verdadeiro,
padrão: 'default2',
foo: 'foo2',
};
});

return import ('../ myOtherModule'). then (myOtherModule => {
// Não estou interessado no módulo simulado myOtherModule, mas no módulo que o utiliza
myModuleToTest.doSomethingToSomeProperty (); // Neste ponto, o módulo original de myOtherModule, e não sua versão simulada, será usado por myModuleToTest
expect (myModuleToTest.someProperty) .toBe ('thisWillFail'); // O teste não passa porque a versão simulada não foi usada
});});

A partir de Jest 26, não há como simular um módulo exportando um objeto (eu
significa algo diferente de uma função) que é usado indiretamente. É isto
correto ou estou faltando alguma coisa? A única solução então é ter mais
de um arquivo de teste para testar o mesmo módulo.

-
Você está recebendo isto porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/facebook/jest/issues/2582#issuecomment-655110424 ou
Cancelar subscrição
https://github.com/notifications/unsubscribe-auth/AAJR3UW6HARW44ZLKAUB7PLR2N77NANCNFSM4C4I7QSQ
.

Esta página foi útil?
0 / 5 - 0 avaliações