Teil III
Über React hinaus
In Teil II haben wir uns nahezu ausschließlich mit der React-API und einigen Standard-APIs des Browsers beschäftigt.
In diesem Teil zeigen wir nun verschiedene Techniken, die üblicherweise in größeren Anwendungen zum Einsatz kommen, aber nicht mehr Bestandteil von React selbst sind. Da React nur eine Bibliothek ist, kannst du dir für viele Problemstellungen selbst aussuchen, ob und was du dafür einsetzen willst.
Die einzelnen Kapitel sind unabhängig voneinander, da es auch nicht notwendig ist, alle vorgestellten Techniken in deiner Anwendung zu verwenden. Du kannst sie alle zusammen einsetzen, es ist aber kein Muss.
8React-Anwendungen testen
Mit dem letzten Kapitel von Teil II haben wir die Entwicklung unserer Anwendung zunächst abgeschlossen. In diesem Kapitel entwickeln wir dafür nun Testfälle, die automatisch ausgeführt werden.
8.1Hands-on: Testen mit Jest und React Testing Library
In diesem Buch schlagen wir dir keine spezielle Teststrategie oder Philosophie vor. Ob du testgetrieben1 arbeitest oder lieber nach getaner Arbeit einzelne Tests schreibst, ist egal. Beide Ansätze sind mit den vorhandenen Möglichkeiten, React-Anwendungen zu testen, gut zu vereinbaren. Und wenn du gar keine Tests schreiben möchtest, können wir das auch sehr gut verstehen.
Schritt 1: Unit Tests mit Jest
Bevor wir zum Testen unserer React-Komponenten kommen, möchten wir zunächst die Logik unserer Anwendung testen, die sich nicht innerhalb einer Komponente befindet. Dadurch lernen wir das zugrunde liegende Testframework Jest2 kennen, ohne auf die React-Besonderheiten eingehen zu müssen.
Unser Projekt ist für das Schreiben und Ausführen dank Create React App bereits vorbereitet. Hier ist Jest schon installiert und sucht anhand einiger Konventionen nach JavaScript-Dateien, die Testcode enthalten. In unserem Projekt legen wir die Testdateien in ein Verzeichnis mit dem Namen __tests__. Dieses Verzeichnis befindet sich jeweils unterhalb des Verzeichnisses, in dem die Module liegen, die wir testen wollen, also zum Beispiel src/components/__tests__. Die eigentlichen Testdateien enden dann auf .test.js. Mit diesem Namen-Pattern wird Jest die Tests automatisch finden und ausführen.
Zunächst schreiben wir Tests für die voteListReducer-Funktion, die wir für die Verwaltung des Zustands in der VoteListPage verwenden. Diese Reducer-Funktion erwartet den aktuellen State und eine Action. Nach der Verarbeitung der Action liefert die Funktion dann einen neuen Zustand zurück. Sowohl Zustand als auch Actions sind einfache JavaScript-Objekte. Reducer-Funktionen sind pure Funktionen, also seiteneffektfrei und ohne Abhängigkeiten zu React. Damit eignen sie sich sehr gut für Unit Tests.
Damit wir im Test auf die Funktion zugreifen können, müssen wir diese zunächst in der Datei VoteListPage.js exportieren:
export function voteListReducer(state, action) { ... }
export default function VoteListPage() { ... }
Die Tests für die Funktion legen wir in der Datei src/components/__tests__/voteReducer.test.js ab.
Wenn die VoteListPage beginnt, die Umfragen vom Server zu laden, löst sie eine START_REQUEST-Action aus. Diese Action enthält keine weiteren Parameter. Die Reducer-Funktion soll daraufhin im State die loading-Eigenschaft auf true setzen. Die korrekte Verarbeitung der Action können wir testen, indem wir die Reducer-Funktion mit der Action aufrufen und das zurückgelieferte Objekt überprüfen:
import { voteListReducer } from "../VoteListPage";
test("handle START_REQUEST action correctly", () => {
const oldState = {};
const newState = voteListReducer(
oldState, { type: "START_REQUEST" });
expect(newState).toEqual({
loading: true
});
});
Testfunktionen
In Jest werden Tests als Funktionen geschrieben. Jeder Test beginnt mit der Funktion test (alternativ: it). Diese Funktion nimmt den sprechenden Namen des Tests entgegen und als weiteren Parameter eine Funktion, die den eigentlichen Test enthält.
Bedingungen überprüfen: expect
Die Bedingungen, die innerhalb eines Tests überprüft werden sollen, werden der expect-Funktion von Jest übergeben. Diese liefert ein Objekt zurück, auf dem eine Reihe von Matcher-Funktionen3 definiert sind. Mit diesen kannst du den Wert überprüfen. Die API ist auf diese Weise fast sprechend: Ich erwarte, dass dieser Wert A identisch ist mit X/größer ist als Y/nicht null ist ...
In unserem obigen Testfall erzeugen wir einen fiktiven aktuellen Zustand und übergeben diesen mitsamt dem Action-Objekt an die Reducer-Funktion (genauso, wie es React auch tun würde). Das Ergebnis vergleichen wir dann mit expect und dem toEqual-Matcher. Auf die gleiche Weise können wir das Verhalten bei anderen Ausgangszuständen oder auch Actions testen.
Schritt 2:...