Manuelle Mocks
This page was AI-translated by PageTurner (beta). Not officially endorsed by the project. Found an error? Report issue →
Manuelle mocks brukes for å erstatte funksjonalitet med testdata. For eksempel, i stedet for å aksessere eksterne ressurser som nettsteder eller databaser, kan du opprette en manuell mock som lar deg bruke falske data. Dette sikrer at testene dine blir raske og forutsigbare.
Mocking av brukermoduler
Manuelle mocks defineres ved å skrive en modul i en __mocks__/-undermappe rett ved siden av modulen. For å mocke en modul kalt user i models-katalogen, oppretter du en fil kalt user.js og plasserer den i models/__mocks__-katalogen.
__mocks__-mappen skiller mellom store og små bokstaver, så hvis du kaller katalogen __MOCKS__, vil dette feile på enkelte systemer.
Når vi inkluderer modulen i testene våre (dvs. vi ønsker å bruke den manuelle mocken istedenfor den faktiske implementasjonen), er det nødvendig å eksplisitt kalle jest.mock('./moduleName').
Mocking av Node-moduler
Hvis modulen du mocker er en Node-modul (f.eks. lodash), skal mocken plasseres i __mocks__-katalogen ved siden av node_modules (med mindre du har konfigurert roots til å peke til en annen mappe enn prosjektroten) og vil bli automatisk mocket. Du trenger ikke eksplisitt å kalle jest.mock('module_name').
Scopede moduler (også kjent som scoped packages) kan mockes ved å opprette en fil i en katalogstruktur som matcher navnet til den scopede modulen. For å mocke en scopet modul kalt @scope/project-name, oppretter du en fil på __mocks__/@scope/project-name.js, og oppretter @scope/-katalogen tilsvarende.
Hvis vi ønsker å mocke Nodes innebygde moduler (f.eks. fs eller path), er det nødvendig å eksplisitt kalle f.eks. jest.mock('path'), fordi innebygde moduler ikke mockes som standard.
Eksempler
.
├── config
├── __mocks__
│ └── fs.js
├── models
│ ├── __mocks__
│ │ └── user.js
│ └── user.js
├── node_modules
└── views
Når en manuell mock eksisterer for en gitt modul, vil Jest-modulsystemet bruke den når jest.mock('moduleName') kalles eksplisitt. Men når automock er satt til true, vil den manuelle mock-implementasjonen bli brukt istedenfor den automatisk opprettede mocken, selv om jest.mock('moduleName') ikke er kalt. For å fravike denne oppførselen må du eksplisitt kalle jest.unmock('moduleName') i tester som skal bruke den faktiske modulimplementasjonen.
For at mocking skal fungere korrekt, krever Jest at jest.mock('moduleName') er i samme skop som require/import-setningen.
Her er et konstruert eksempel der vi har en modul som gir en sammendrag av alle filene i en gitt katalog. I dette tilfellet bruker vi den innebygde fs-modulen.
'use strict';
const fs = require('fs');
function summarizeFilesInDirectorySync(directory) {
return fs.readdirSync(directory).map(fileName => ({
directory,
fileName,
}));
}
exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;
Siden vi ønsker at testene våre skal unngå faktisk diskaksess (som er tregt og skjørt), oppretter vi en manuell mock for fs-modulen ved å utvide en automatisk mock. Vår manuelle mock implementerer egendefinerte versjoner av fs-APIene vi kan bygge på i testene:
'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;
Nå skriver vi testen vår. I dette tilfellet må jest.mock('fs') kalles eksplisitt fordi fs er Nodes innebygde modul:
'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);
});
});
Eksempelet vist her bruker jest.createMockFromModule for å generere en automatisk mock, og overstyrer dens standardoppførsel. Dette er den anbefalte tilnærmingen, men er helt valgfri. Hvis du ikke ønsker å bruke den automatiske mocken i det hele tatt, kan du eksportere egne funksjoner fra mockfilen. En ulempe med fullstendig manuelle mocks er at de er manuelle – det vil si at du må manuelt oppdatere dem hver gang modulen de mocker endres. Derfor er det best å bruke eller utvide den automatiske mocken når den fungerer for dine behov.
For å sikre at en manuell mock og dens faktiske implementasjon forblir synkronisert, kan det være nyttig å hente den faktiske modulen ved hjelp av jest.requireActual(moduleName) i din manuelle mock og utvide den med mock-funksjoner før eksport.
Eksempelkoden for dette finner du på examples/manual-mocks.
Bruk med ES-modulimport
Hvis du bruker ES-modulimport, plasserer du vanligvis import-setninger øverst i testfilen. Men ofte m å du instruere Jest om å bruke en mock før moduler benytter den. Av denne grunn vil Jest automatisk flytte jest.mock-kall til toppen av modulen (før noen importer). For å lære mer og se dette i praksis, sjekk dette repoet.
jest.mock-kall kan ikke flyttes til toppen av modulen hvis du har aktivert støtte for ECMAScript-moduler. ESM-modullasteren evaluerer alltid statiske importer før kodeutførelse. Se ECMAScriptModules for detaljer.
Mocking av metoder som ikke er implementert i JSDOM
Hvis kode bruker en metode som JSDOM (Jests DOM-implementasjon) ikke støtter ennå, blir testing vanskelig. Dette gjelder for eksempel window.matchMedia(). Jest gir da feilen TypeError: window.matchMedia is not a function og utfører ikke testen korrekt.
I slike tilfeller løser det seg vanligvis ved å mocke matchMedia i testfilen:
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(),
})),
});
Dette fungerer hvis window.matchMedia() brukes i en funksjon som kalles under testen. Hvis window.matchMedia() kjøres direkte i den testede filen, vil Jest fortsatt rapportere samme feil. Løsningen er da å flytte den manuelle mocken til en egen fil og inkludere denne i testen før den testede filen:
import './matchMedia.mock'; // Must be imported before the tested file
import {myMethod} from './file-to-test';
describe('myMethod()', () => {
// Test the method here...
});