Heim > Artikel > Web-Frontend > Einführung in Jest: Unit-Tests, Mocking und asynchroner Code
Jest ist eine Bibliothek zum Testen von JavaScript-Code.
Es handelt sich um ein von Facebook verwaltetes Open-Source-Projekt, das sich besonders gut zum Testen von React-Code eignet, aber nicht darauf beschränkt ist: Es kann jeden JavaScript-Code testen. Seine Stärken sind:
export default function sum(a, n) { return a + b; }
divide.test.js
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
Ein Matcher ist eine Methode, mit der Sie Werte testen können.
Eine Abhängigkeit ist ein Codeabschnitt, von dem Ihre Anwendung abhängt. Es könnte sich um eine Funktion/ein Objekt in unserem Projekt oder eine Abhängigkeit eines Drittanbieters (z. B. Axios) handeln
Ein Code wird zu einer echten Abhängigkeit, wenn Ihre eigene Anwendung ohne ihn nicht funktionieren kann.
Zum Beispiel, wenn Sie in Ihrer Anwendung eine Funktion implementieren, um E-Mails zu senden, API-Anfragen zu stellen oder ein Konfigurationsobjekt zu erstellen usw.
Es gibt zwei Möglichkeiten, wie wir in unserem Code in einem js-Projekt Abhängigkeiten hinzufügen können:
export default function sum(a, n) { return a + b; }
Nur ein schicker Begriff für ein einfaches Konzept.
Wenn Ihre Funktion einige Funktionen von einer externen Abhängigkeit benötigt, fügen Sie sie einfach als Argument ein.
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
Unit-Tests werden von Softwareentwicklern geschrieben und ausgeführt, um sicherzustellen, dass ein Abschnitt einer Anwendung (bekannt als „Einheit“) seinem Design entspricht und sich wie vorgesehen verhält.
Wir möchten unseren Code isoliert testen, die tatsächliche Implementierung etwaiger Abhängigkeiten ist uns egal.
Wir möchten
Und hier kommt die Verspottung unserer Abhängigkeiten ins Spiel.
Bei Unit-Tests bieten uns Mocks die Fähigkeit, die von einer Abhängigkeit bereitgestellte Funktionalität zu unterdrücken und eine Möglichkeit, zu beobachten, wie unser Code mit der Abhängigkeit interagiert.
Mocks sind besonders nützlich, wenn es teuer oder unpraktisch ist, eine Abhängigkeit direkt in unsere Tests einzubeziehen, beispielsweise in Fällen, in denen Ihr Code HTTP-Aufrufe an eine API durchführt oder mit der Datenbankschicht interagiert.
Es ist vorzuziehen, die Antworten für diese Abhängigkeiten auszublenden und gleichzeitig sicherzustellen, dass sie nach Bedarf aufgerufen werden. Hier kommen Mocks zum Einsatz.
Durch die Verwendung von Scheinfunktionen können wir Folgendes wissen:
Es gibt mehrere Möglichkeiten, Scheinfunktionen zu erstellen.
Die jest.fn-Methode ist für sich genommen eine Funktion höherer Ordnung.
Es handelt sich um eine Factory-Methode, die neue, ungenutzte Scheinfunktionen erstellt.
Funktionen in JavaScript sind erstklassige Bürger, sie können als Argumente weitergegeben werden.
Jede Scheinfunktion hat einige besondere Eigenschaften. Die Mock-Eigenschaft ist von grundlegender Bedeutung. Bei dieser Eigenschaft handelt es sich um ein Objekt, das alle Scheinstatusinformationen darüber enthält, wie die Funktion aufgerufen wurde. Dieses Objekt enthält drei Array-Eigenschaften:
export default function sum(a, n) { return a + b; }
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
Diese Art der Verspottung kommt aus mehreren Gründen seltener vor:
Ein häufigerer Ansatz ist die Verwendung von jest.mock um alle Exporte eines Moduls automatisch auf die Mock-Funktion festzulegen.
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
Manchmal möchten Sie nur zusehen, wie eine Methode aufgerufen wird, aber die ursprüngliche Implementierung beibehalten. In anderen Fällen möchten Sie möglicherweise die Implementierung nachahmen, das Original jedoch später in der Suite wiederherstellen.
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
Stellen Sie die ursprüngliche Implementierung wieder her
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
JavaScript ist Single-Threaded:Es kann immer nur eine Aufgabe gleichzeitig ausgeführt werden. Normalerweise ist das keine große Sache, aber jetzt stellen Sie sich vor, Sie führen eine Aufgabe aus, die 30 Sekunden dauert. Ja. Während dieser Aufgabe warten wir 30 Sekunden, bevor etwas anderes passieren kann (JavaScript wird standardmäßig im Hauptthread des Browsers ausgeführt. daher bleibt die gesamte Benutzeroberfläche hängen).
Wir schreiben das Jahr 2020, niemand möchte eine langsame, nicht reagierende Website.
Glücklicherweise bietet uns der Browser einige Funktionen, die die JavaScript-Engine selbst nicht bietet: eine Web-API. Dazu gehören die DOM-API, setTimeout, HTTP-Anfragen und so weiter. Dies kann uns helfen, ein gewisses asynchrones, nicht blockierendes Verhalten
zu schaffen
export default function sum(a, n) { return a + b; }
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
Jest geht normalerweise davon aus, dass die Funktionen der Tests synchron ausgeführt werden.
Wenn wir einen asynchronen Vorgang durchführen, Jest aber nicht mitteilen, dass er auf das Ende des Tests warten soll, wird ein falsch positives Ergebnis ausgegeben.
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
Asynchrone Muster
Es gibt verschiedene Muster für die Handhabung asynchroner Vorgänge in JavaScript. Die am häufigsten verwendeten sind:
Sie können keinen Test in einem Rückruf haben, da Jest ihn nicht ausführt – die Ausführung der Testdatei endet, bevor der Rückruf aufgerufen wird. Um dies zu beheben, übergeben Sie einen Parameter an die Testfunktion, den Sie bequem als erledigt aufrufen können. Jest wartet, bis Sie done() aufrufen, bevor es den Test beendet:
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
Bei Funktionen, die Versprechen zurückgeben, geben wir ein Versprechen aus dem Test zurück:
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
Um Funktionen zu testen, die Versprechen zurückgeben, können wir auch async/await verwenden, was die Syntax sehr unkompliziert und einfach macht:
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
Das obige ist der detaillierte Inhalt vonEinführung in Jest: Unit-Tests, Mocking und asynchroner Code. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!