Heim > Web-Frontend > js-Tutorial > Einführung in Jest: Unit-Tests, Mocking und asynchroner Code

Einführung in Jest: Unit-Tests, Mocking und asynchroner Code

Linda Hamilton
Freigeben: 2024-11-01 00:23:28
Original
694 Leute haben es durchsucht

Introduction to Jest: Unit Testing, Mocking, and Asynchronous Code

Einführung in Jest

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:

  • es geht schnell
  • es kann Snapshot-Tests durchführen
  • Es ist eigensinnig und bietet alles sofort einsatzbereit, ohne dass Sie Entscheidungen treffen müssen
export default function sum(a, n) {
  return a + b;
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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);
});
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Matcher

Ein Matcher ist eine Methode, mit der Sie Werte testen können.

  • toBe vergleicht strikte Gleichheit mit ===
  • toEqual vergleicht die Werte zweier Variablen. Wenn es sich um ein Objekt oder Array handelt, prüft es die Gleichheit aller Eigenschaften oder Elemente
  • toBeNull ist wahr, wenn ein Nullwert übergeben wird
  • toBeDefined ist wahr, wenn ein definierter Wert übergeben wird (im Gegensatz zu oben)
  • toBeUndefined ist wahr, wenn ein undefinierter Wert übergeben wird
  • toBeCloseTo wird verwendet, um Gleitkommawerte zu vergleichen und Rundungsfehler zu vermeiden
  • toBeTruthy wahr, wenn der Wert als wahr angesehen wird (wie es bei einem „Wenn“ der Fall ist)
  • toBeFalsy wahr, wenn der Wert als falsch angesehen wird (wie es bei einem „Wenn“ der Fall ist)
  • toBeGreaterThan wahr, wenn das Ergebnis von Expect() höher ist als das Argument
  • toBeGreaterThanOrEqual wahr, wenn das Ergebnis von Expect() gleich dem Argument oder höher als das Argument ist
  • toBeLessThan wahr, wenn das Ergebnis von Expect() kleiner ist als das Argument
  • toBeLessThanOrEqual wahr, wenn das Ergebnis von Expect() gleich dem Argument oder kleiner als das Argument ist
  • toMatch wird verwendet, um Zeichenfolgen mit dem Mustervergleich regulärer Ausdrücke zu vergleichen
  • toContain wird in Arrays verwendet, true, wenn das erwartete Array das Argument in seiner Elementmenge enthält
  • toHaveLength(number): prüft die Länge eines Arrays
  • toHaveProperty(key, value): prüft, ob ein Objekt eine Eigenschaft hat, und prüft optional seinen Wert
  • toThrow prüft, ob eine von Ihnen übergebene Funktion eine Ausnahme (allgemein) oder eine bestimmte Ausnahme auslöst
  • toBeInstanceOf(): prüft, ob ein Objekt eine Instanz einer Klasse ist

Abhängigkeiten

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:

Importe

export default function sum(a, n) {
  return a + b;
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Abhängigkeitsinjektion

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);
});
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Unit-Tests

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

verifizieren
  • dass unsere Codeeinheit wie erwartet funktioniert
  • gibt die erwarteten Ergebnisse zurück
  • ruft alle Mitarbeiter (Abhängigkeiten) auf, wie es sollte

Und hier kommt die Verspottung unserer Abhängigkeiten ins Spiel.

Verspottung

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:

  • Die Anzahl der eingegangenen Anrufe.
  • ArgumentWerte, die bei jedem Aufruf verwendet werden.
  • Der „Kontext“ oder dieser Wert bei jedem Aufruf.
  • Wie die Funktion beendet wurde und welche Werte erzeugt wurden.

Spott im Scherz

Es gibt mehrere Möglichkeiten, Scheinfunktionen zu erstellen.

  • Mit der Methode jest.fn können wir direkt eine neue Scheinfunktion erstellen.
  • Wenn Sie eine Objektmethode verspotten, können Sie jest.spyOn verwenden.
  • Und wenn Sie ein ganzes Modul verspotten möchten, können Sie jest.mock verwenden.

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:

  • Anrufe [Argumente jedes Anrufs]
  • Instanzen ['dieser' Wert bei jedem Aufruf]
  • Ergebnisse [der Wert, den die Funktion beendet hat], die Ergebnisseigenschaft hat einen Typ (Rückgabe oder Wurf) und einen Wert
    • Die Funktion gibt explizit einen Wert zurück.
    • Die Funktion wird ohne Return-Anweisung vollständig ausgeführt (was der Rückgabe von undefiniert entspricht
    • ).
    • Die Funktion gibt einen Fehler aus.
export default function sum(a, n) {
  return a + b;
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  • https://codesandbox.io/s/implementing-mock-functions-tkc8b

Schein-Basic

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);
});
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Verspottung injizierter Abhängigkeiten

import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
Nach dem Login kopieren
Nach dem Login kopieren

Spottmodule

Verspotten Sie eine Funktion mit jest.fn

// 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);
}

Nach dem Login kopieren
Nach dem Login kopieren

Diese Art der Verspottung kommt aus mehreren Gründen seltener vor:

  • jest.mock erledigt dies automatisch für alle Funktionen in einem Modul
  • jest.spyOn macht das Gleiche, ermöglicht aber die Wiederherstellung der ursprünglichen Funktion

Verspotten Sie ein Modul mit jest.mock

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;
}
Nach dem Login kopieren
Nach dem Login kopieren

Spionieren oder verspotten Sie eine Funktion mit jest.spyOn

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");
});
Nach dem Login kopieren
Nach dem Login kopieren

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);
});
Nach dem Login kopieren
Nach dem Login kopieren

Javascript und die Ereignisschleife

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;
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  • Call Stack – Wenn wir eine Funktion aufrufen, wird sie zu etwas hinzugefügt, das Call Stack genannt wird.
  • WebAPI – setTimeout wird von der WebAPI bereitgestellt, übernimmt eine Rückruffunktion und richtet einen Timer ein, ohne den Hauptthread zu blockieren
  • Warteschlange – wenn der Timer abgelaufen ist, wird der Rückruf zur Warteschlange hinzugefügt
  • Ereignisschleife – prüft, ob der Aufrufstapel leer ist, prüft, ob in der Warteschlange auszuführende Rückrufe vorhanden sind, und wechselt zum auszuführenden Aufrufstapel
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);
});
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Testen von asynchronem Code mit Jest

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';
Nach dem Login kopieren
Nach dem Login kopieren

Asynchrone Muster
Es gibt verschiedene Muster für die Handhabung asynchroner Vorgänge in JavaScript. Die am häufigsten verwendeten sind:

  • Rückrufe
  • Versprechen & Async/Warten

Rückrufe testen

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);
}

Nach dem Login kopieren
Nach dem Login kopieren

Versprechen

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;
}
Nach dem Login kopieren
Nach dem Login kopieren

Asynchron/warten

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");
});
Nach dem Login kopieren
Nach dem Login kopieren

Tipps

  • Wir müssen ein gutes Verständnis dafür haben, was unsere Funktion tut und was wir testen werden
  • Denken Sie über das Verhalten des Codes nach, den wir testen
  • Bereiten Sie die Bühne:
    • Abhängigkeiten nachahmen/ausspionieren
    • Interagiert unser Code mit globalen Objekten? wir können sie auch verspotten/ausspionieren
    • Interagieren unsere Tests mit dem DOM? Wir können einige gefälschte Elemente erstellen, mit denen wir arbeiten können
    • Strukturieren Sie Ihre Tests
    • Gegeben ...
    • Wenn ich anrufe ....
    • Dann ... erwarte ich .....
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);
});
Nach dem Login kopieren
Nach dem Login kopieren

Links

  • https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c
  • https://jestjs.io/docs/en/mock-functions
  • https://codesandbox.io/s/implementing-mock-functions-tkc8b
  • https://github.com/BulbEnergy/jest-mock-examples
  • https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
  • https://jestjs.io/docs/en/asynchronous
  • https://www.pluralsight.com/guides/test-asynchronous-code-jest

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!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage