Testen von Komponenten in Reaktion mit Jest und Enzym

Dies ist der zweite Teil der Serie zum Testen von Komponenten in Reaktion. Wenn Sie bereits Erfahrung mit Jest haben, können Sie den GitHub-Code als Ausgangspunkt verwenden. 

Im vorigen Artikel haben wir die grundlegenden Prinzipien und Ideen der testgetriebenen Entwicklung behandelt. Wir haben auch die Umgebung und die Tools eingerichtet, die zum Ausführen von Tests in React erforderlich sind. Das Toolset umfasste Jest, ReactTestUtils, Enzyme und React-Test-Renderer. 

Wir haben dann ein paar Tests für eine Demoanwendung mit ReactTestUtils geschrieben und deren Nachteile im Vergleich zu einer robusteren Bibliothek wie Enzyme entdeckt.

In diesem Beitrag erhalten Sie ein tieferes Verständnis für das Testen von Komponenten in React, indem Sie praktischere und realistischere Tests schreiben. Sie können zu GitHub gehen und mein Repo klonen, bevor Sie loslegen.

Erste Schritte mit der Enzym-API

Enzyme.js ist eine Open-Source-Bibliothek, die von Airbnb verwaltet wird, und ist eine großartige Ressource für React-Entwickler. Es verwendet die ReactTestUtils-API darunter, aber im Gegensatz zu ReactTestUtils bietet Enzyme eine API auf hoher Ebene und eine leicht verständliche Syntax. Installieren Sie Enzyme, falls Sie dies noch nicht getan haben.

Die Enzym-API exportiert drei Arten von Rendering-Optionen:

  1. flaches Rendern
  2. Volles DOM-Rendering
  3. statisches Rendern

Flache Wiedergabe wird verwendet, um eine bestimmte Komponente isoliert darzustellen. Die untergeordneten Komponenten werden nicht gerendert, sodass Sie ihr Verhalten nicht durchsetzen können. Wenn Sie sich auf Unit-Tests konzentrieren, werden Sie dies lieben. Sie können eine Komponente wie folgt flach rendern:

import flach aus 'Enzym'; importiere ProductHeader von './ProductHeader'; // Konkretes Beispiel unten. const-Komponente = flach (); 

Volles DOM-Rendering erzeugt ein virtuelles DOM der Komponente mit Hilfe einer Bibliothek namens jsdom. Sie können diese Funktion nutzen, indem Sie das ersetzen flach() Methode mit montieren() im obigen Beispiel. Der offensichtliche Vorteil ist, dass Sie die untergeordneten Komponenten auch rendern können. Wenn Sie das Verhalten einer Komponente mit ihren untergeordneten Elementen testen möchten, sollten Sie dies verwenden. 

Statisches Rendering wird verwendet, um reagierte Komponenten in statisches HTML zu rendern. Es wurde mit einer Bibliothek namens Cheerio implementiert. Weitere Informationen dazu finden Sie in den Dokumenten. 

Überprüfung unserer vorherigen Tests

Hier sind die Tests, die wir im letzten Tutorial geschrieben haben:

src / components / __ tests __ / ProductHeader.test.js

ReactTestUtils aus 'Reakt-Dom / Test-Utils' importieren; // ES6 beschreiben ('ProductHeader Component'), () => it ('hat ein h2-Tag', () => const-Komponente = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithTag (Komponente, 'h2'); ); it ('hat eine Titelklasse'), () => const component = ReactTestUtils .renderIntoDocument (); var node = ReactTestUtils .findRenderedDOMComponentWithClass (Komponente, 'title'); ))

Der erste Test prüft, ob die ProducerHeader Komponente hat eine

tag, und der zweite ermittelt, ob eine CSS-Klasse benannt wurde Titel. Der Code ist schwer zu lesen und zu verstehen. 

Hier sind die Tests, die mit Enzyme neu geschrieben wurden.

src / components / __ tests __ / ProductHeader.test.js

import shallow aus 'enzym' beschreiben ('ProductHeader Component', () => it ('hat ein h2-Tag', () => const component = shallow (); var node = komponente.find ('h2'); Expect (node.length) .toEqual (1); ); es ('hat eine Titelklasse'), () => const Komponente = flach (); var node = komponente.find ('h2'); Expect (node.hasClass ('title')). toBeTruthy (); ))

Zuerst habe ich ein flach erstelltes DOM des erstellt  Komponente mit flach() und in einer Variablen gespeichert. Dann habe ich die verwendet .finden() Methode, um einen Knoten mit dem Tag 'h2' zu finden. Es fragt das DOM ab, ob es eine Übereinstimmung gibt. Da es nur eine Instanz des Knotens gibt, können wir sicher davon ausgehen Knotenlänge wird gleich 1 sein.

Der zweite Test ist dem ersten sehr ähnlich. Das hasClass ('title') Die Methode gibt zurück, ob der aktuelle Knoten eine Klassenname Requisite mit dem Wert "Titel". Wir können die Wahrhaftigkeit mit überprüfen toBeTruthy ().  

Führen Sie die Tests mit aus Garntest, und beide Tests sollten bestehen. 

Gut gemacht! Jetzt ist es an der Zeit, den Code zu überarbeiten. Dies ist aus Sicht eines Testers wichtig, da lesbare Tests einfacher zu warten sind. In den obigen Tests sind die ersten beiden Zeilen für beide Tests identisch. Sie können sie mit a umgestalten beforeEach () Funktion. Wie der Name schon sagt, die vor jedem Funktion wird einmal aufgerufen, bevor jede Spezifikation in einem Beschreibungsblock ausgeführt wird. 

Sie können eine Pfeilfunktion an übergeben beforeEach () so was.

src / components / __ tests __ / ProductHeader.test.js

import shallow aus 'enzym' beschreiben ('ProductHeader Component', () => Komponente, Knoten lassen; // Jest beforeEach () beforeEach () (> (=) => Komponente = flach ())) beforeEach ((())>> node = component.find ('h2'))) it ('hat ein h2-Tag', () => expect (node) .toBeTruthy ()); it ('hat eine Titelklasse', () => expect (node.hasClass ('title')). toBeTruthy ()))

Schreiben von Unit-Tests mit Jest und Enzym

Schreiben wir ein paar Unit-Tests für die Produktdetails Komponente. Es ist eine Präsentationskomponente, die die Details jedes einzelnen Produkts anzeigt. 

Wir werden den hervorgehobenen Abschnitt testen

Der Unit-Test versucht die folgenden Annahmen zu bestätigen:

  • Die Komponente existiert und die Requisiten werden weitergereicht.
  • Die Requisiten wie Produktname, Beschreibung und Verfügbarkeit werden angezeigt.
  • Eine Fehlermeldung wird angezeigt, wenn die Requisiten leer sind.

Hier ist die Grundstruktur des Tests. Der Erste beforeEach () speichert die Produktdaten in einer Variablen, und die zweite hängt die Komponente.

src / components / __ tests __ / ProductDetails.test.js

beschreiben ("ProductDetails-Komponente", () => var-Komponente, Produkt; beforeEach (()) => product = id: 1, Name: 'NIKE Liteforce Blue Sneakers'), Beschreibung: 'Lorem ipsum.', Status: 'Verfügbar';) beforeEach (() => Komponente = Mount (); ) it ('test # 1', () => ))

Der erste Test ist einfach:

it ('sollte existieren', () => expect (Komponente) .toBeTruthy (); Expect (component.props (). product) .toEqual (product);)

Hier benutzen wir die Requisiten() Diese Methode ist praktisch, um die Requisiten einer Komponente zu erhalten.

Für den zweiten Test können Sie Elemente nach ihren Klassennamen abfragen und dann prüfen, ob der Produktname, die Beschreibung usw. Teil des Elements sind innerText

 it ('sollte Produktdaten anzeigen, wenn Requisiten übergeben werden'), () => let title = komponente.find ('. product-title'); erwarten (title.text ()). toEqual (product.name); description = komponente.find ('. product-description'); erwarten (description.text ()). toEqual (product.description);) 

Das Text() Die Methode ist in diesem Fall besonders nützlich, um den inneren Text eines Elements abzurufen. Schreiben Sie eine Erwartung für die Produktstatus () und sehen, ob alle Tests bestanden sind.

Für den abschließenden Test werden wir den montieren Produktdetails Komponente ohne Requisiten. Dann suchen wir nach einer Klasse mit dem Namen ".product-error" und prüfen, ob sie den Text "Entschuldigung, Produkt ist nicht vorhanden" enthält..

 it ('sollte einen Fehler anzeigen, wenn Requisiten nicht übergeben werden', () => / * Komponente ohne Requisiten * / component = mount (); let node = komponente.find ('. product-error'); expect (node.text ()). toEqual ('Sorry. Produkt existiert nicht'); ) 

Das ist es. Wir haben das erfolgreich getestet Komponente isoliert. Tests dieser Art werden als Unit-Tests bezeichnet.

Rückrufe mit Stubs und Spionen testen

Wir haben gerade gelernt, Requisiten zu testen. Um eine Komponente jedoch wirklich isoliert testen zu können, müssen Sie auch die Callback-Funktionen testen. In diesem Abschnitt schreiben wir Tests für die Produktliste Komponente und erstellen Sie Stubs für Rückruffunktionen auf dem Weg. Hier sind die Annahmen, die wir behaupten müssen.

  1. Die Anzahl der aufgeführten Produkte sollte der Anzahl der Objekte entsprechen, die die Komponente als Requisiten erhält.
  2. Klicken Sie auf sollte die Rückruffunktion aufrufen.

Lass uns einen erstellen beforeEach () Funktion, die Modellproduktdaten für unsere Tests ausfüllt.

src / components / __ tests __ / ProductList.test.js

 beforeEach (() => productData = [id: 1, Name: 'NIKE Liteforce Blue Sneakers', Beschreibung: 'Lorem ipsu.', Status: 'Verfügbar', // Aus Gründen der Kürze])

Lassen Sie uns nun unsere Komponente in einer anderen montieren beforeEach () Block.

beforeEach (() => handleProductClick = jest.fn (); Komponente = mount (  ); )

Das Produktliste erhält die Produktdaten über Requisiten. Darüber hinaus erhält es einen Rückruf vom übergeordneten Element. Obwohl Sie Tests für die Callback-Funktion der Eltern schreiben könnten, ist dies keine gute Idee, wenn Sie an den Komponententests festhalten möchten. Da die Rückruffunktion zur übergeordneten Komponente gehört, werden die Tests durch das Einbinden der Logik des übergeordneten Elements kompliziert. Stattdessen erstellen wir eine Stub-Funktion.

Was ist ein Stummel?? 

Ein Stub ist eine Dummy-Funktion, die vorgibt, eine andere Funktion zu sein. Auf diese Weise können Sie eine Komponente unabhängig testen, ohne übergeordnete oder untergeordnete Komponenten zu importieren. Im obigen Beispiel haben wir eine Stub-Funktion erstellt handleProductClick durch Anrufen jest.fn ()

Jetzt müssen wir nur noch alles finden Elemente im DOM und simulieren Sie einen Klick auf das erste Knoten. Nach dem Anklicken prüfen wir, ob handleProductClick () wurde angerufen. Wenn ja, kann man sagen, dass unsere Logik wie erwartet funktioniert.

it ('sollte selectProduct aufrufen, wenn angeklickt wird ", () => const firstLink = komponente.find (' a '). first (); firstLink.simulate (' click '); Expect (handleProductClick.mock.calls.length) .toEqual (1);))

Mit Enzym können Sie Benutzeraktionen wie Klicks mit simulieren simulieren() Methode. handlerProductClick.mock.calls.length Gibt die Anzahl zurück, wie oft die Mock-Funktion aufgerufen wurde. Wir erwarten, dass es gleich 1 ist.

Der andere Test ist relativ einfach. Du kannst den ... benutzen finden() Methode, um alle abzurufen Knoten im DOM. Die Anzahl von Die Knoten sollten der Länge des productData-Arrays entsprechen, das wir zuvor erstellt haben. 

 it ('sollte alle Produktelemente anzeigen', () => let links = component.find ('a'); erwarten (links.length) .toEqual (productData.length);) 

Testen des Zustands der Komponente, des LifeCycleHook und der Methode

Als nächstes werden wir das testen Produktcontainer Komponente. Es hat einen Status, einen Lebenszyklus-Hook und eine Klassenmethode. Hier sind die Aussagen, die überprüft werden müssen:

  1. componentDidMount wird genau einmal aufgerufen.
  2. Der Zustand der Komponente wird aufgefüllt, nachdem die Komponente bereitgestellt wurde.
  3. Das handleProductClick () Die Methode sollte den Status aktualisieren, wenn eine Produkt-ID als Argument übergeben wird.

Um zu prüfen, ob componentDidMount hieß, wir werden es ausspionieren. Im Gegensatz zu einem Stub wird ein Spion verwendet, wenn Sie eine vorhandene Funktion testen möchten. Sobald der Spion eingestellt ist, können Sie Zusicherungen schreiben, um zu bestätigen, ob die Funktion aufgerufen wurde.

Sie können eine Funktion wie folgt ausspionieren:

src / components / __ tests __ / ProductContainer.test.js

 it ('sollte componentDidMount einmal aufrufen'), () => componentDidMountSpy = spyOn (ProductContainer.prototype, 'componentDidMount'); // fertig werden);

Der erste Parameter zu jest.spyOn ist ein Objekt, das den Prototyp der Klasse definiert, die wir ausspionieren. Der zweite ist der Name der Methode, die wir ausspionieren möchten. 

Rendern Sie nun die Komponente und erstellen Sie eine Bestätigung, um zu überprüfen, ob der Spion aufgerufen wurde.

 Komponente = flach (); Expect (componentDidMountSpy) .toHaveBeenCalledTimes (1);

Um zu überprüfen, ob der Status der Komponente nach dem Mounten der Komponente aufgefüllt ist, können wir die Enzyme verwenden Zustand() Methode, um alles im Zustand abzurufen. 

it ('sollte den Zustand bevölkern'), () => Komponente = flach (); Expect (Komponente.Status (). ProductList.length) .toEqual (4))

Der dritte ist etwas knifflig. Wir müssen das überprüfen handleProductClick funktioniert wie erwartet. Wenn Sie zum Code hinübergehen, sehen Sie das handleProductClick () Die Methode verwendet eine Produkt-ID als Eingabe und wird dann aktualisiert this.state.selectedProduct mit den Details dieses Produkts. 

Um dies zu testen, müssen Sie die Methode der Komponente aufrufen, und Sie können dies tatsächlich durch Aufrufen tun component.instance (). handleProductClick (). Wir übergeben eine Musterprodukt-ID. Im folgenden Beispiel verwenden wir die ID des ersten Produkts. Dann können wir testen, ob der Status aktualisiert wurde, um zu bestätigen, dass die Zusicherung wahr ist. Hier ist der ganze Code:

 it ('sollte eine Arbeitsmethode namens handleProductClick haben', () => let firstProduct = productData [0] .id; Komponente = flach (); component.instance (). handleProductClick (firstProduct); expect (component.state (). selectedProduct) .toEqual (productData [0]); ) 

Wir haben 10 Tests geschrieben, und wenn alles gut geht, sollten Sie Folgendes sehen:

Zusammenfassung

Puh! Wir haben fast alles behandelt, was Sie wissen müssen, um Tests in React mit Jest und Enzyme zu schreiben. Jetzt ist es vielleicht ein guter Zeitpunkt, um auf die Enzyme-Website zu gelangen, um ihre API genauer zu untersuchen.

Was denken Sie über das Schreiben von Tests in React? Ich würde sie gerne in den Kommentaren hören.