Ir para o conteúdo principal
Versão: 30.0

Mocks Manuais

Tradução Beta Não Oficial

Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →

Mocks manuais são usados para substituir funcionalidades com dados falsos. Por exemplo, em vez de acessar um recurso remoto como um site ou banco de dados, você pode criar um mock manual que permite usar dados fictícios. Isso garante que seus testes sejam rápidos e confiáveis.

Mockando módulos de usuário

Mocks manuais são definidos escrevendo um módulo em um subdiretório __mocks__/ adjacente ao módulo original. Por exemplo, para criar um mock de um módulo chamado user no diretório models, crie um arquivo user.js dentro do diretório models/__mocks__.

cuidado

O diretório __mocks__ diferencia maiúsculas/minúsculas, então nomeá-lo como __MOCKS__ causará erros em alguns sistemas.

"message": "nota"

Quando requisitamos esse módulo em nossos testes (ou seja, queremos usar o mock manual em vez da implementação real), é obrigatório chamar explicitamente jest.mock('./moduleName').

Mockando módulos do Node

Se o módulo sendo mockado é um módulo Node (ex: lodash), o mock deve ficar no diretório __mocks__ adjacente a node_modules (a menos que você tenha configurado roots para apontar para outra pasta) e será mockado automaticamente. Não é necessário chamar jest.mock('module_name') explicitamente.

Módulos com escopo (também chamados de scoped packages) podem ser mockados criando um arquivo numa estrutura de diretórios que corresponda ao nome do módulo. Por exemplo, para mockar um módulo @scope/project-name, crie um arquivo em __mocks__/@scope/project-name.js, criando o diretório @scope/ conforme necessário.

cuidado

Se quisermos mockar módulos nativos do Node (ex: fs ou path), então chamar explicitamente jest.mock('path') é obrigatório, pois módulos internos não são mockados por padrão.

Exemplos

.
├── config
├── __mocks__
│   └── fs.js
├── models
│   ├── __mocks__
│   │   └── user.js
│   └── user.js
├── node_modules
└── views

Quando existe um mock manual para um módulo, o sistema de módulos do Jest usará esse mock quando jest.mock('moduleName') for chamado explicitamente. Porém, quando automock está definido como true, a implementação do mock manual será usada no lugar do mock automático, mesmo sem chamar jest.mock('moduleName'). Para desativar esse comportamento, chame explicitamente jest.unmock('moduleName') nos testes que devem usar a implementação real.

"message": "informação"

Para o mock funcionar corretamente, o jest.mock('moduleName') precisa estar no mesmo escopo que a declaração require/import.

Eis um exemplo ilustrativo onde temos um módulo que resume todos os arquivos em um diretório. Neste caso, usamos o módulo nativo fs:

FileSummarizer.js
'use strict';

const fs = require('fs');

function summarizeFilesInDirectorySync(directory) {
return fs.readdirSync(directory).map(fileName => ({
directory,
fileName,
}));
}

exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;

Como queremos evitar acesso real ao disco (que é lento e frágil), criamos um mock manual para fs estendendo um mock automático. Nosso mock manual implementará versões personalizadas das APIs do fs:

__mocks__/fs.js
'use strict';

const path = require('path');

const fs = jest.createMockFromModule('fs');

// This is a custom function that our tests can use during setup to specify
// what the files on the "mock" filesystem should look like when any of the
// `fs` APIs are used.
let mockFiles = Object.create(null);
function __setMockFiles(newMockFiles) {
mockFiles = Object.create(null);
for (const file in newMockFiles) {
const dir = path.dirname(file);

if (!mockFiles[dir]) {
mockFiles[dir] = [];
}
mockFiles[dir].push(path.basename(file));
}
}

// A custom version of `readdirSync` that reads from the special mocked out
// file list set via __setMockFiles
function readdirSync(directoryPath) {
return mockFiles[directoryPath] || [];
}

fs.__setMockFiles = __setMockFiles;
fs.readdirSync = readdirSync;

module.exports = fs;

Agora escrevemos nosso teste. Neste caso, jest.mock('fs') deve ser chamado explicitamente porque fs é um módulo nativo do Node:

__tests__/FileSummarizer-test.js
'use strict';

jest.mock('fs');

describe('listFilesInDirectorySync', () => {
const MOCK_FILE_INFO = {
'/path/to/file1.js': 'console.log("file1 contents");',
'/path/to/file2.txt': 'file2 contents',
};

beforeEach(() => {
// Set up some mocked out file info before each test
require('fs').__setMockFiles(MOCK_FILE_INFO);
});

test('includes all files in the directory in the summary', () => {
const FileSummarizer = require('../FileSummarizer');
const fileSummary =
FileSummarizer.summarizeFilesInDirectorySync('/path/to');

expect(fileSummary.length).toBe(2);
});
});

O exemplo mostrado usa jest.createMockFromModule para gerar um mock automático e sobrescreve seu comportamento padrão. Esta é a abordagem recomendada, mas é opcional. Se você não quiser usar o mock automático, pode exportar suas próprias funções diretamente. A desvantagem de mocks totalmente manuais é que eles exigem atualizações manuais sempre que o módulo mockado mudar. Por isso, é melhor usar ou estender o mock automático quando possível.

Para garantir que um mock manual e sua implementação real permaneçam sincronizados, pode ser útil importar o módulo real usando jest.requireActual(moduleName) no seu mock manual e aprimorá-lo com funções simuladas antes de exportá-lo.

O código para este exemplo está disponível em examples/manual-mocks.

Uso com imports de módulos ES

Se você está usando imports de módulos ES, normalmente inclinará a colocar suas declarações import no topo do arquivo de teste. Porém, frequentemente é necessário instruir o Jest a usar um mock antes que os módulos o utilizem. Por essa razão, o Jest automaticamente eleva as chamadas jest.mock para o topo do módulo (antes de qualquer import). Para saber mais e ver isso em ação, consulte este repositório.

cuidado

Chamadas jest.mock não podem ser elevadas para o topo do módulo se você habilitou suporte a módulos ECMAScript. O carregador de módulos ESM sempre avalia os imports estáticos antes de executar o código. Veja ECMAScriptModules para detalhes.

Simulando métodos não implementados no JSDOM

Se algum código utiliza um método que o JSDOM (a implementação DOM usada pelo Jest) ainda não implementou, testá-lo não é trivial. É o caso, por exemplo, de window.matchMedia(). O Jest retorna TypeError: window.matchMedia is not a function e não executa o teste corretamente.

Nesse caso, simular matchMedia no arquivo de teste deve resolver o problema:

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});

Isso funciona se window.matchMedia() for usado numa função (ou método) invocado durante o teste. Se window.matchMedia() for executado diretamente no arquivo testado, o Jest reportará o mesmo erro. Nessa situação, a solução é mover o mock manual para um arquivo separado e incluí-lo no teste antes do arquivo testado:

import './matchMedia.mock'; // Must be imported before the tested file
import {myMethod} from './file-to-test';

describe('myMethod()', () => {
// Test the method here...
});