Heim  >  Artikel  >  Web-Frontend  >  Lassen Sie uns Jest viel schneller laufen lassen

Lassen Sie uns Jest viel schneller laufen lassen

Patricia Arquette
Patricia ArquetteOriginal
2024-10-31 12:23:54544Durchsuche

Let’s Make Jest Run Much Faster

Aber zuerst müssen wir verstehen, warum es so langsam ist.

Praxisbeispiel

Stellen Sie sich eine einfache React-Komponente vor.

import React from "react";
import { deepClone } from "./utils";

export function App() {
  const obj = { foo: 'bar' };

  return (
    <div>
      <p>Object looks like this: {JSON.stringify(deepClone(obj))}</p>
    </div>
  );
}

App-Komponente hängt nur von einer Dienstprogrammfunktion ab –  deepClone. Die Utils-Datei sieht so aus.

import _ from 'lodash';
import moment from 'moment';
import * as mui from '@mui/material';

export const deepClone = (obj) => _.cloneDeep(obj);
export const getFormattedDate = (date) => moment(date).format('YYYY-MM-DD');

export const isButton = (instance) => instance === mui.Button;

Es werden drei einzeilige Hilfsfunktionen exportiert. Das ist es.
Hier ist nun eine große Frage: Wie lange wird es Ihrer Meinung nach dauern, diesen Test durchzuführen?

import React from "react";
import { render, screen } from "@testing-library/react";
import { App } from "./app";
import "@testing-library/jest-dom";

test("renders the app", () => {
  render(<App />);
});

Die Antwort? Eine Ewigkeit!

 PASS  src/tests/react-app/react-app.test.js
  √ renders the date and sum correctly (25 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.045 s

Auf meinem Computer dauerte es 5 Sekunden, um einen einzeiligen Testfall für eine einzeilige React-Komponente auszuführen.

Analyse der Leistung

Um zu analysieren, was hinter den Kulissen passiert, können wir entweder den Profiler von Chrome verwenden - Ich empfehle, dieses aufschlussreiche Video von Kent C. Dodds anzusehen.
Alternativ können Sie eine Jest-Neat-Runner-Bibliothek verwenden, die den Profilerstellungsprozess vereinfacht. Setzen Sie die Option NEAT_REPORT_MODULE_LOAD_ABOVE_MS auf 150 und aktivieren Sie NEAT_REPORT_TRANSFORM. Diese Konfiguration druckt die Module aus, deren Laden mehr als 150 ms dauert, und gibt Informationen darüber, wie lange es gedauert hat, die Dateien zu verarbeiten (öffnen und transpilieren).

Lassen Sie uns Letzteres verwenden. Dies ist die Ausgabe.

> jest src/tests/react-app/

From src\tests\react-app\utils.js -> @mui/material in 1759ms
From node_modules\@mui\material\node\styles\adaptV4Theme.js -> @mui/system in 509ms
From src\tests\react-app\react-app.test.js -> @testing-library/react in 317ms
From node_modules\@testing-library\react\dist\pure.js -> @testing-library/dom in 266ms
From node_modules\@mui\system\ThemeProvider\ThemeProvider.js -> @mui/private-theming in 166ms
From node_modules\@testing-library\dom\dist\role-helpers.js -> aria-query in 161ms

Wir laden die Bibliothek „@mui/material“ fast 2 Sekunden lang, ohne sie überhaupt zu verwenden!

Grundursache in vielen Projekten?

Chaotische Abhängigkeiten

Meiner Erfahrung nach sind Leistungsprobleme bei Jest hauptsächlich auf die große Anzahl transitiver Abhängigkeiten zurückzuführen, die zur Laufzeit nicht einmal verwendet werden. Wie in unserem obigen Beispiel gezeigt, geraten Sie möglicherweise in die gleiche Situation wie ich, wenn Sie nicht ausreichend darauf achten, welche Dateien Sie in Ihre Anwendung importieren.

In meinem Fall hängt die App-Komponente nur von der Dienstprogrammfunktion deepClone ab. Da jedoch deepClone aus der utils-Datei exportiert wird, wurden auch alle Abhängigkeiten innerhalb der utils-Datei mit geladen.

Dateien, die viele lose verwandte Funktionen und starke Abhängigkeiten enthalten, können Ihre Anwendung und Tests erheblich verlangsamen.

Fassfeilen

Jest ist kein Freund von ESM-Modulen, was dazu führt, dass es auf CommonJS zurückgreift. Folglich funktioniert das Baumrütteln nicht richtig. Dies ist besonders problematisch, wenn man sich auf Module verlässt, die aus Barrel-Dateien (Indexdateien) importiert wurden.
Wenn Sie beispielsweise eine kleine Komponente oder Funktion aus einer Barrel-Datei importieren, lädt Jest auch alles andere - was offensichtlich unnötigen Overhead verursacht.

Was nun?

Manuelles Anpassen der Importstrategie

Abgesehen vom Entfernen der Barrel-Dateien und der Umgestaltung der gesamten Codebasis durch Aufteilen von Dateien mit zahlreichen Abhängigkeiten in kleinere, fokussiertere Module. Wir können Module identifizieren, deren Laden lange dauert, und nach kleineren Alternativmodulen suchen oder prüfen, ob das importierte Modul einzelne Teile separat exportiert (d. h. benannte Importe), anstatt die Barrel-Datei zu verwenden.
Bedeutung, statt

import React from "react";
import { deepClone } from "./utils";

export function App() {
  const obj = { foo: 'bar' };

  return (
    <div>
      <p>Object looks like this: {JSON.stringify(deepClone(obj))}</p>
    </div>
  );
}

tun

import _ from 'lodash';
import moment from 'moment';
import * as mui from '@mui/material';

export const deepClone = (obj) => _.cloneDeep(obj);
export const getFormattedDate = (date) => moment(date).format('YYYY-MM-DD');

export const isButton = (instance) => instance === mui.Button;

Wenn wir das Modul überhaupt nicht verwenden, können wir es über jest.mock verspotten, um zu vermeiden, dass es vollständig geladen wird.
Allerdings können diese Anpassungen recht zeitaufwändig sein.

Laufzeit-Cache-Ansatz

Eine effektivere Methode ist die Verwendung der Jest-Neat-Runner-Bibliothek mit der Option NEAT_RUNTIME_CACHE. Wenn diese Option aktiviert ist, verfolgt die Bibliothek die tatsächliche Laufzeitnutzung aller Module (pro Testdatei) und speichert Abhängigkeiten, die wir für nachfolgende Testläufe nicht benötigen, in einem Cache. Lassen Sie mich anhand des obigen Beispiels zeigen, was es bewirkt

import React from "react";
import { render, screen } from "@testing-library/react";
import { App } from "./app";
import "@testing-library/jest-dom";

test("renders the app", () => {
  render(<App />);
});

Wir haben die Ausführungszeit von fünf auf zwei Sekunden reduziert, indem wir das Laden von 26 unnötigen Bibliotheken, einschließlich der MUI-Bibliothek, übersprungen haben.
Seien Sie vorsichtig  – es gibt mehrere Einschränkungen bei der Verwendung von NEAT_RUNTIME_CACHE. Lesen Sie daher unbedingt die README-Datei, bevor Sie es verwenden.

Andere Optimierungstechniken

Transpilierungsoptimierungen: Untersuchen Sie, wie viele Dateien transpiliert werden müssen, und verwenden Sie den effektivsten Transpiler (wie SWC oder esbuild). Wenn Sie Zeit sparen möchten, bietet die Option NEAT_REPORT_TRANSFORM in jest-neat-runner detaillierte Informationen darüber, wie viel Zeit und wie viele Module zum Transpilieren erforderlich sind.

Module im Speicher zwischenspeichern: Standardmäßig speichert Jest keine Module im Speicher, was bedeutet, dass bei jedem Testlauf das Modul geöffnet, analysiert und in den Speicher geladen werden muss. Wenn Sie über eine umfangreiche Testsuite und genügend Speicher verfügen, sollten Sie die Option NEAT_TRANSFORM_CACHE in Betracht ziehen, um die Arbeit zu beschleunigen.

Was ist mit der CI-Pipeline?

Parallele Ausführungen: CircleCI und GitHub Actions unterstützen parallele Ausführungen. Das bedeutet, dass Sie mehr Maschinen hochfahren und die Last mithilfe des Shard-Parameters in Jest aufteilen können.
Speichern des Jest- und Neat-Cache: Dies ist entscheidend für die Nutzung von Jest und Jest-Neat-Runner im CI. Stellen Sie sicher, dass Sie in Jest die Option cacheDirectory festgelegt haben. Speichern Sie dann das Verzeichnis nach dem Testlauf und stellen Sie den Cache wieder her, bevor Sie die Tests ausführen. Vorsichtsmaßnahme: Wenn Sie Parallelität verwenden, stellen Sie sicher, dass Sie für jeden Knoten eindeutige Caches speichern. CircleCI stellt beispielsweise die Umgebungsvariable CIRCLE_NODE_INDEX bereit, die Sie beim Speichern des Caches nutzen können. So sieht es im CircleCI aus.

import React from "react";
import { deepClone } from "./utils";

export function App() {
  const obj = { foo: 'bar' };

  return (
    <div>
      <p>Object looks like this: {JSON.stringify(deepClone(obj))}</p>
    </div>
  );
}

Indem Sie diese Richtlinien befolgen, können Sie die Leistung von Jest in Ihren Projekten erheblich steigern.

Das obige ist der detaillierte Inhalt vonLassen Sie uns Jest viel schneller laufen lassen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
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