Manual Mocks
Mocks, ou simulações, manuais são usadas para esboçar funcionalidade com dados simulados. Por exemplo, em vez de acessar um recurso remoto como um site ou um banco de dados, convém criar uma simulação manual que permite a utilização de dados falsos. Isso garante que os seus testes serão rápidos e não fragmentados.
Simulações manuais são definidas por escrever um módulo em um subdiretório __mocks__/
imediatamente adjacente ao módulo. Por exemplo, para simular (mock, em inglês) um módulo chamado user
no diretório models
, crie um arquivo chamado user.js
e coloque ele no diretório models/__mocks__
. Se o módulo que você está simulando (mocking, em inglês) é um módulo do Node (por exemplo: fs
), a simulação deve ser colocada no diretório __mocks__
adjacente ao node_modules
. Por exemplo:
.
├── config
├── __mocks__
│ └── fs.js
├── models
│ ├── __mocks__
│ │ └── user.js
│ └── user.js
├── node_modules
└── views
Quando uma simulação manual existe para um determinado módulo, o sistema de módulo do Jest usará esse módulo ao chamar explicitamente jest.mock('moduleName')
. No entanto, simulações manuais terão precedência sobre módulos Node mesmo se jest.mock('moduleName')
não é chamado. Para desativar esse comportamento, você precisará chamar explicitamente jest.unmock('moduleName')
em testes que devem usar a implementação do efetiva do módulo.
Aqui está um exemplo inventado, onde temos um módulo que fornece um resumo de todos os arquivos em um determinado diretório.
// FileSummarizer.js 'use strict'; const fs = require('fs'); function summarizeFilesInDirectorySync(directory) { return fs.readdirSync(directory).map(fileName => ({ directory, fileName, })); } exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;
Já que gostaríamos de evitar que nossos testes tenham que acessar o disco (isso é muito lento e frágil), criamos uma simulação (mock, em inglês) manual para o módulo fs
por estender uma simulação automática. Nossa simulação manual irá implementar versões personalizadas das APIs fs
que podemos nos basear para nossos testes:
// __mocks__/fs.js 'use strict'; const path = require('path'); const fs = jest.genMockFromModule('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 podemos escrever nosso teste:
// __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 de simulação (mock, em inglês) mostrado aqui usa jest.genMockFromModule
para gerar uma simulação automática e substitui seu comportamento padrão. Esta é a abordagem recomendada, mas é completamente opcional. Se você não quiser de forma alguma usar a simulação automática, você pode simplesmente exportar suas próprias funções do arquivo de simulação. Uma desvantagem para as simulações totalmente manuais é que elas são manuais – ou seja, você tem que atualizar elas manualmente toda vez que o módulo que elas estão simulando mudar. Por causa disso, é melhor usar ou estender a simulação automática quando ela funciona para suas necessidades.
Para garantir que uma simulação manual e sua implementação real permaneçam em sincronia, pode ser útil exigir o módulo real usando require.requireActual(moduleName)
em sua simulação manual e modificá-la com funções de simulação antes de exportá-la.
O código deste exemplo está disponível em examples/manual_mocks.
Usando com imports de módulo ES #
Se você estiver usando imports de módulo ES então você normalmente vai ser inclinado a colocar suas declarações de import
na parte superior do arquivo de teste. Mas muitas vezes você precisa instruir Jest para usar uma simulação (mock, em inglês) antes que módulos usem ela. Por esta razão, Jest içará (hoist, em inglês) automaticamente chamadas jest.mock
para o topo do módulo (antes de quaisquer importações). Para saber mais sobre isso e ver em ação, veja este repo.