Manuelle Mocks
Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →
Manuelle Mocks werden verwendet, um Funktionalität mit Mock-Daten zu ersetzen. Anstatt beispielsweise auf eine entfernte Ressource wie eine Website oder Datenbank zuzugreifen, kannst du einen manuellen Mock erstellen, der dir die Verwendung von Testdaten ermöglicht. Dies stellt sicher, dass deine Tests schnell und zuverlässig sind.
Eigene Module mocken
Manuelle Mocks werden erstellt, indem ein Modul in einem __mocks__/ Unterverzeichnis direkt neben dem zu mockenden Modul platziert wird. Um beispielsweise ein Modul namens user im Verzeichnis models zu mocken, erstellst du eine Datei user.js im Verzeichnis models/__mocks__.
Der __mocks__-Ordner ist case-sensitive. Wenn du das Verzeichnis __MOCKS__ nennst, funktioniert es auf einigen Systemen nicht.
Wenn wir dieses Modul in unseren Tests verwenden (d.h. den manuellen Mock statt der echten Implementierung nutzen wollen), muss jest.mock('./moduleName') explizit aufgerufen werden.
Node-Module mocken
Wenn es sich um ein Node-Modul handelt (z.B. lodash), sollte der Mock im __mocks__-Verzeichnis neben node_modules platziert werden (sofern roots nicht auf ein anderes Verzeichnis zeigt). Solche Mocks werden automatisch verwendet – ein expliziter Aufruf von jest.mock('module_name') ist nicht nötig.
Scoped Modules (auch bekannt als Scoped Packages) können durch eine Dateistruktur gemockt werden, die dem Modulnamen entspricht. Um beispielsweise @scope/project-name zu mocken, legst du eine Datei unter __mocks__/@scope/project-name.js an und erstellst dabei entsprechend das @scope/-Verzeichnis.
Für Node-Core-Module (z.B. fs oder path) muss explizit jest.mock('path') aufgerufen werden, da diese Standardmodule nicht automatisch gemockt werden.
Beispiele
.
├── config
├── __mocks__
│ └── fs.js
├── models
│ ├── __mocks__
│ │ └── user.js
│ └── user.js
├── node_modules
└── views
Wenn ein manueller Mock existiert, verwendet Jests Modulsystem ihn bei explizitem Aufruf von jest.mock('moduleName'). Ist jedoch automock auf true gesetzt, wird der manuelle Mock auch ohne Aufruf von jest.mock('moduleName') verwendet. Um dieses Verhalten zu deaktivieren, rufe jest.unmock('moduleName') in Tests auf, die die Originalimplementierung benötigen.
Damit Mocks korrekt funktionieren, muss jest.mock('moduleName') im gleichen Scope stehen wie die require/import-Anweisung.
Hier ein konstruiertes Beispiel: Ein Modul liefert eine Zusammenfassung aller Dateien in einem Verzeichnis. Dabei nutzen wir das Core-Modul fs.
'use strict';
const fs = require('fs');
function summarizeFilesInDirectorySync(directory) {
return fs.readdirSync(directory).map(fileName => ({
directory,
fileName,
}));
}
exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;
Da unsere Tests nicht auf die Festplatte zugreifen sollen (langsam und fehleranfällig), erstellen wir einen manuellen Mock für fs, der einen automatischen Mock erweitert. Unser Mock implementiert angepasste Versionen der fs-APIs für Testzwecke:
'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;
Nun schreiben wir unseren Test. Hier muss jest.mock('fs') explizit aufgerufen werden, da fs ein Node-Core-Modul ist:
'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);
});
});
Das Beispiel verwendet jest.createMockFromModule zur Generierung eines automatischen Mocks und überschreibt dessen Standardverhalten. Dieser Ansatz wird empfohlen, ist aber optional. Wenn du den automatischen Mock nicht verwenden möchtest, kannst du eigene Funktionen aus der Mock-Datei exportieren. Ein Nachteil vollständig manueller Mocks ist ihre Wartung – sie müssen bei Änderungen des Originalmoduls manuell angepasst werden. Daher ist es meist besser, den automatischen Mock zu verwenden oder zu erweitern.
Um sicherzustellen, dass ein manueller Mock und seine tatsächliche Implementierung synchron bleiben, kann es hilfreich sein, das echte Modul in deinem manuellen Mock mit jest.requireActual(moduleName) zu laden, es mit Mock-Funktionen zu erweitern und dann zu exportieren.
Der Code für dieses Beispiel ist verfügbar unter examples/manual-mocks.
Verwendung mit ES-Modul-Imports
Wenn du ES-Modul-Imports verwendest, neigst du normalerweise dazu, import-Anweisungen an den Anfang der Testdatei zu setzen. Oft musst du Jest jedoch anweisen, einen Mock zu verwenden, bevor Module ihn nutzen. Aus diesem Grund hebt Jest jest.mock-Aufrufe automatisch an den Anfang des Moduls (vor alle Imports). Weitere Details und Beispiele findest du in diesem Repo.
jest.mock-Aufrufe können nicht an den Anfang des Moduls gehoisted werden, wenn du ECMAScript-Modulunterstützung aktiviert hast. Der ESM-Modullader wertet statische Imports immer vor der Codeausführung aus. Siehe ECMAScriptModules für Details.
Mocken von Methoden ohne JSDOM-Implementierung
Wenn Code eine Methode verwendet, die JSDOM (die von Jest verwendete DOM-Implementierung) noch nicht implementiert hat, ist das Testen nicht einfach möglich. Dies trifft beispielsweise auf window.matchMedia() zu. Jest wirft dann TypeError: window.matchMedia is not a function und führt den Test nicht ordnungsgemäß aus.
In diesem Fall sollte das Mocken von matchMedia in der Testdatei das Problem lösen:
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(),
})),
});
Dies funktioniert, wenn window.matchMedia() in einer Funktion (oder Methode) verwendet wird, die im Test aufgerufen wird. Wenn window.matchMedia() direkt in der getesteten Datei ausgeführt wird, meldet Jest denselben Fehler. In diesem Fall besteht die Lösung darin, den manuellen Mock in eine separate Datei auszulagern und diese im Test vor der getesteten Datei einzubinden:
import './matchMedia.mock'; // Must be imported before the tested file
import {myMethod} from './file-to-test';
describe('myMethod()', () => {
// Test the method here...
});