Eine bequeme Möglichkeit zum Testen von React-Komponenten

Ich habe einen benutzerdefinierten Berichts-Builder für Jest geschrieben und auf GitHub veröffentlicht . Mein Builder heißt Jest-Snapshots-Book. Er erstellt ein HTML-Buch mit Snapshots der Komponenten einer React-Anwendung.



In diesem Artikel wird erläutert, was Jest ist, Schnappschuss-Tests, für die im Allgemeinen ein zusätzlicher Berichtsersteller erforderlich ist, und wie diese geschrieben werden. Grundsätzlich gilt dies alles für das Testen von React-Komponenten, aber theoretisch kann es angewendet werden, wenn mit serialisierbaren Daten gearbeitet wird.

Paginatorreaktionskomponente


In diesem Artikel wird beispielsweise die Paginatorkomponente ( Paginator ) getestet . Es ist Teil unseres Projekts zum Erstellen von Anwendungen ohne Server in AWS ( GitHub ). Die Aufgabe einer solchen Komponente besteht darin, Schaltflächen zum Navigieren durch die Seiten einer Tabelle oder etwas anderes anzuzeigen.

Dies ist eine einfache Funktionskomponente ohne zustandslose Komponente (zustandslose Komponente). Als Eingabe erhält es von Requisiten die Gesamtzahl der Seiten, die aktuelle Seite und die Handlerfunktion beim Klicken auf die Seite. Am Ausgang erzeugt die Komponente einen gebildeten Paginator. Zum Anzeigen der Schaltflächen wird eine andere untergeordnete Schaltflächenkomponente verwendet. Wenn es viele Seiten gibt, zeigt der Paginator nicht alle an, kombiniert sie und zeigt sie in Form einer Ellipse an.



Paginator-Komponentencode
import React from 'react'; import classes from './Paginator.css'; import Button from '../../UI/Button/Button'; const Paginator = (props) => { const { tp, cp, pageClickHandler } = props; let paginator = null; if (tp !== undefined && tp > 0) { let buttons = []; buttons.push( <Button key={`pback`} disabled={cp === 1} clicked={(cp === 1 ? null : event => pageClickHandler(event, 'back'))}> ← </Button> ); const isDots = (i, tp, cp) => i > 1 && i < tp && (i > cp + 1 || i < cp - 1) && (cp > 4 || i > 5) && (cp < tp - 3 || i < tp - 4); let flag; for (let i = 1; i <= tp; i++) { const dots = isDots(i, tp, cp) && (isDots(i - 1, tp, cp) || isDots(i + 1, tp, cp)); if (flag && dots) { flag = false; buttons.push( <Button key={`p${i}`} className={classes.Dots} disabled={true}> ... </Button> ); } else if (!dots) { flag = true; buttons.push( <Button key={`p${i}`} disabled={i === cp} clicked={(i === cp ? null : event => pageClickHandler(event, i))}> {i} </Button> ); } } buttons.push( <Button key={`pforward`} disabled={cp === tp} clicked={(cp === tp ? null : event => pageClickHandler(event, 'forward'))}> → </Button> ); paginator = <div className={classes.Paginator}> {buttons} </div> } return paginator; } export default Paginator; 
Schaltflächenkomponentencode
 import React from 'react'; import classes from './Button.css'; const button = (props) => ( <button disabled={props.disabled} className={classes.Button + (props.className ? ' ' + props.className : '')} onClick={props.clicked}> {props.children} </button> ); export default button; 

Scherz


Jest ist eine bekannte OpenSource-Bibliothek zum Testen von JavaScript-Code. Es wurde dank Facebook erstellt und entwickelt. Geschrieben in Node.js.

Im Allgemeinen hängt die Bedeutung des Testens davon ab, dass Sie Eingabeparameter für Ihren Code erstellen und sofort die Ausgabe beschreiben müssen, die Ihr Code erzeugen soll. Bei der Durchführung von Tests führt Jest Ihren Code mit Eingabeparametern aus und vergleicht das Ergebnis mit dem erwarteten. Wenn es übereinstimmt, wird der Test bestanden, und wenn nicht, wird er nicht bestanden.

Ein kleines Beispiel von jestjs.io .

Angenommen, wir haben ein Node.js-Modul, eine Funktion, die zwei Zahlen hinzufügt ( sum.js- Datei):

 function sum(a, b) { return a + b; } module.exports = sum; 

Wenn unser Modul in einer Datei gespeichert ist, müssen wir zum Testen die Datei sum.test.js erstellen, in die der Code zum Testen geschrieben werden soll:

 const sum = require('./sum'); test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); 

In diesem Beispiel haben wir mithilfe der Testfunktion einen Test mit dem Namen "Addiert 1 + 2 zu 3" erstellt . Als zweiten Parameter für die Testfunktion übergeben wir eine Funktion, die den Test tatsächlich ausführt.

Der Test besteht darin, dass wir unsere Summenfunktion mit den Eingabeparametern 1 und 2 ausführen und das Ergebnis an die Funktion Jest expected () übergeben . Dann wird unter Verwendung der Funktion Jest toBe () das übertragene Ergebnis mit dem erwarteten ( 3 ) verglichen. Die Funktion toBe () gehört zur Kategorie der Jest (Matcher) -Testfunktionen .

Gehen Sie zum Testen einfach in den Projektordner und rufen Sie jest in der Befehlszeile auf. Jest findet die Datei mit der Erweiterung .test.js und führt den Test aus. Hier ist das Ergebnis, das er ausgeben wird:

 PASS ./sum.test.js ✓ adds 1 + 2 to equal 3 (5ms) 

Enzym- und Schnappschuss-Test von Komponenten


Snapshot-Tests sind eine relativ neue Funktion in Jest. Der Punkt ist, dass wir Jest mithilfe einer speziellen Testfunktion bitten, einen Snapshot unserer Datenstruktur auf der Festplatte zu speichern und bei nachfolgenden Testläufen neue Snapshots mit zuvor gespeicherten zu vergleichen.

Der Schnappschuss ist in diesem Fall nichts anderes als eine Textdarstellung der Daten. Ein Snapshot eines Objekts sieht beispielsweise folgendermaßen aus (der Array-Schlüssel hier ist der Name des Tests):

 exports[`some test name`] = ` Object { "Hello": "world" } `; 

So sieht die Jest-Testfunktion aus, die einen Bildvergleich durchführt (optionale Parameter):

 expect(value).toMatchSnapshot(propertyMatchers, snapshotName) 

Der Wert kann eine beliebige serialisierbare Datenstruktur sein. Zum ersten Mal schreibt die Funktion toMatchSnapshot () den Snapshot einfach auf die Festplatte und führt in späteren Zeiten den Vergleich bereits durch.

Am häufigsten wird diese Testtechnologie speziell zum Testen von React-Komponenten und noch genauer zum Testen des korrekten Renderings von React-Komponenten verwendet. Dazu müssen Sie die Komponente nach dem Rendern als Wert übergeben .

Enzyme ist eine Bibliothek, die das Testen von React-Anwendungen erheblich vereinfacht, indem sie praktische Funktionen zum Rendern von Komponenten bietet. Enzym wird bei Airbnb entwickelt.

Mit Enzyme können Sie Komponenten im Code rendern. Hierfür gibt es mehrere praktische Funktionen, die unterschiedliche Renderoptionen ausführen:

  • vollständiges Rendern (wie im Browser vollständiges DOM-Rendern);
  • vereinfachtes Rendern (flaches Rendern);
  • statisches Rendern

Wir werden uns nicht mit den Rendering-Optionen befassen. Für Snapshot-Tests ist statisches Rendering ausreichend, damit Sie statischen HTML-Code der Komponente und ihrer untergeordneten Komponenten erhalten können:

 const wrapper = render(<Foo title="unique" />); 

Also rendern wir unsere Komponente und übergeben das Ergebnis an expected () und rufen dann die Funktion .toMatchSnapshot () auf . Die it- Funktion ist nur eine Abkürzung für die Testfunktion .

 ... const wrapper = render(<Paginator tp={tp} cp={cp} />); it(`Total = ${tp}, Current = ${cp}`, () => { expect(wrapper).toMatchSnapshot(); }); ... 

Jedes Mal , wenn der Test ausgeführt wird , vergleicht toMatchSnapshot () zwei Snapshots: erwartet (die zuvor auf die Festplatte geschrieben wurden) und aktuell (die während des aktuellen Tests erhalten wurden).

Wenn die Bilder identisch sind, gilt der Test als bestanden. Wenn es einen Unterschied in den Bildern gibt, gilt der Test als nicht bestanden, und dem Benutzer wird der Unterschied zwischen den beiden Bildern in Form eines Unterschieds angezeigt (wie bei Versionskontrollsystemen).

Hier ist ein Beispiel für die Jest-Ausgabe, wenn der Test fehlschlägt. Hier sehen wir, dass wir im aktuellen Bild eine zusätzliche Schaltfläche haben.



In dieser Situation muss der Benutzer entscheiden, was zu tun ist. Wenn Änderungen am Snapshot aufgrund von Änderungen im Komponentencode geplant sind, muss der alte Snapshot durch einen neuen überschrieben werden. Und wenn die Änderungen unerwartet sind, müssen Sie nach einem Problem in Ihrem Code suchen.

Ich werde ein vollständiges Beispiel zum Testen eines Paginators geben (Datei Paginator.test.js ).

Für bequemere Paginatortests habe ich eine Snapshoot-Funktion (tp, cp) erstellt , die zwei Parameter akzeptiert : die Gesamtzahl der Seiten und die aktuelle Seite. Diese Funktion führt einen Test mit den angegebenen Parametern durch. Sie müssen nur noch die Funktion snapshoot () mit verschiedenen Parametern (auch in einer Schleife) aufrufen und testen, testen ...

 import React from 'react'; import { configure, render } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import Paginator from './Paginator'; configure({ adapter: new Adapter() }); describe('Paginator', () => { const snapshoot = (tp, cp) => { const wrapper = render(<Paginator tp={tp} cp={cp} />); it(`Total = ${tp}, Current = ${cp}`, () => { expect(wrapper).toMatchSnapshot(); }); } snapshoot(0, 0); snapshoot(1, -1); snapshoot(1, 1); snapshoot(2, 2); snapshoot(3, 1); for (let cp = 1; cp <= 10; cp++) { snapshoot(10, cp); } }); 

Warum brauchten Sie einen zusätzlichen Berichts-Generator?


Als ich anfing, mit dieser Testtechnologie zu arbeiten, ließ mich das Gefühl eines unvollendeten anfänglichen Ansatzes nicht los. Bilder können schließlich nur als Text betrachtet werden.

Was aber, wenn eine Komponente beim Rendern viel HTML-Code erzeugt? Hier ist eine 3-Tasten-Paginatorkomponente. Ein Schnappschuss einer solchen Komponente sieht folgendermaßen aus:

 exports[`Paginator Total = 1, Current = -1 1`] = ` <div class="Paginator" > <button class="Button" > ← </button> <button class="Button" > 1 </button> <button class="Button" > → </button> </div> `; 

Zuerst müssen Sie sicherstellen, dass die Originalversion der Komponente korrekt gerendert wird. Es ist nicht sehr bequem, dies einfach durch Anzeigen des HTML-Codes in Textform zu tun. Dies sind jedoch nur drei Tasten. Und wenn Sie zum Beispiel einen Tisch oder etwas noch umfangreicheres testen müssen? Darüber hinaus müssen Sie zum vollständigen Testen viele Bilder anzeigen. Es wird ziemlich unpraktisch und schwierig sein.

Wenn der Test fehlschlägt, müssen Sie verstehen, wie sich das Erscheinungsbild der Komponenten unterscheidet. Der Unterschied in ihrem HTML-Code ermöglicht es Ihnen natürlich zu verstehen, was sich geändert hat, aber auch hier ist die Möglichkeit, den Unterschied persönlich zu sehen, nicht überflüssig.

Im Allgemeinen dachte ich, dass es notwendig wäre, damit die Bilder im Browser in der gleichen Form angezeigt werden können, wie sie in der Anwendung aussehen. Einschließlich mit auf sie angewendeten Stilen. So kam mir die Idee, den Snapshot-Testprozess zu verbessern, indem ich einen zusätzlichen Berichts-Builder für Jest schrieb.

Mit Blick auf die Zukunft habe ich das bekommen. Jedes Mal, wenn ich die Tests ausführe, aktualisiert mein Builder das Snapshot-Buch. Direkt im Browser können Sie die Komponenten so anzeigen, wie sie in der Anwendung aussehen, sowie sofort den Quellcode der Bilder und des Diff anzeigen (falls der Test fehlschlägt).



Jest Report Builder


Die Ersteller von Jest haben die Möglichkeit geboten, zusätzliche Berichtsersteller zu schreiben. Dies geschieht wie folgt. Sie müssen ein Modul auf Node.JS schreiben, das eine oder mehrere der folgenden Methoden aufweisen muss: onRunStart , onTestStart , onTestResult , onRunComplete , die verschiedenen Testfortschrittsereignissen entsprechen.

Dann müssen Sie Ihr Modul in der Jest-Konfiguration verbinden. Hierfür gibt es eine spezielle Reporterrichtlinie . Wenn Sie Ihren Builder zusätzlich einschließen möchten, müssen Sie ihn am Ende des Reporter- Arrays hinzufügen.

Danach ruft Jest in bestimmten Phasen der Testausführung Methoden aus Ihrem Modul auf und übergibt die aktuellen Ergebnisse an die Methoden. Der Code in diesen Methoden sollte tatsächlich zusätzliche Berichte erstellen, die Sie benötigen. Im Allgemeinen sieht die Erstellung zusätzlicher Berichtsersteller so aus.

Wie Jest-Snapshots-Buch funktioniert


Ich füge den Modulcode nicht speziell in den Artikel ein, da ich ihn weiter verbessern werde. Es befindet sich auf meinem GitHub. Dies ist die Datei src / index.js auf der Projektseite.

Mein Berichtsersteller wird nach Abschluss der Tests aufgerufen. Ich habe den Code in die Methode onRunComplete (Kontexte, Ergebnisse) eingefügt . Es funktioniert wie folgt.

In der Eigenschaft results.testResults übergibt der Jest ein Array von Testergebnissen an diese Funktion. Jedes Testergebnis enthält einen Pfad zur Testdatei und eine Reihe von Nachrichten mit den Ergebnissen. Mein Berichts-Generator sucht nach jeder Testdatei mit einer entsprechenden Snapshot-Datei. Wenn eine Snapshot-Datei erkannt wird, erstellt der Berichts-Generator eine HTML-Seite im Snapshot-Buch und schreibt sie in den Snapshot-Buchordner im Projektstammordner.

Um eine HTML-Seite zu generieren , sammelt der Berichts- Generator mithilfe der rekursiven Funktion grabCSS (moduleName, css = [], level = 0) alle Stile, beginnend mit der zu testenden Komponente und weiter unten im Baum aller importierten Komponenten. Somit sammelt die Funktion alle Stile, die für die korrekte Anzeige der Komponente erforderlich sind. Gesammelte Stile werden der HTML-Seite des Snapshot-Buches hinzugefügt.

Ich verwende CSS-Module in meinen Projekten, daher bin ich mir nicht sicher, ob dies funktioniert, wenn keine CSS-Module verwendet werden.

Wenn der Test bestanden ist, fügt der Builder einen iFrame mit dem Bild in zwei Anzeigeoptionen in die HTML-Seite ein: den Quellcode (das Bild wie es ist) und die Komponente nach dem Rendern. Die Anzeigeoption in iFrame wird durch Klicken mit der Maus geändert.

Wenn der Test nicht bestanden wurde, ist alles komplizierter. Jest liefert in diesem Fall nur die Meldung, die in der Konsole angezeigt wird (siehe Abbildung oben).

Es enthält Unterschiede und zusätzliche Informationen zum fehlgeschlagenen Test. In diesem Fall handelt es sich im Wesentlichen um zwei Bilder: das erwartete und das tatsächliche . Wenn wir das erwartete haben - es wird auf der Festplatte im Snapshot-Ordner gespeichert, dann bietet der aktuelle Snapshot-Jest keine Informationen.

Daher musste ich Code schreiben, der den aus der Nachricht entnommenen Jest-Diff auf den erwarteten Snapshot anwendet und einen tatsächlichen Snapshot basierend auf dem erwarteten erstellt. Danach zeigt der Builder neben dem iFrame den erwarteten iFrame-Snapshot des aktuellen Snapshots an, dessen Inhalt zwischen drei Optionen geändert werden kann: Quellcode, Komponente nach dem Rendern, Diff.

So sieht die Ausgabe des Berichts-Generators aus, wenn Sie die Option verbose = true festlegen.



Nützliche Links



PS


Snapshot-Tests reichen nicht aus, um eine React-Anwendung vollständig zu testen. Es wird nur das Rendern Ihrer Komponenten behandelt. Es ist auch notwendig, ihre Funktion zu testen (z. B. Reaktionen auf Benutzeraktionen). Snapshot-Tests sind jedoch eine sehr bequeme Methode, um sicherzustellen, dass Ihre Komponenten wie beabsichtigt gerendert werden. Und Jest-Snapshots-Book erleichtert den Vorgang ein wenig.

Source: https://habr.com/ru/post/de421647/


All Articles