iX 2/2019
S. 48
Titel
Webentwicklung
Aufmacherbild

React-Anwendungen mit Jest und Enzyme testen

Seitentest

Mit den Testwerkzeugen Jest und Enzyme können Entwickler einfach Unit-Tests für Webanwendungen erstellen, die das JavaScript-Framework React nutzen.

Das Frontend-Framework React, 2013 von Facebook veröffentlicht, ist neben Googles Angular und dem Open-Source-Projekt Vue.js heute die beliebteste Bibliothek zur Erstellung von Single-Page-Anwendungen. Kernmerkmal ist die hierarchische Gliederung in einzelne Komponenten, dank der Aktionen und Zuständigkeiten sauber getrennt sind: Jede Komponente ist für genau eine Aufgabe zuständig.

Eine besondere Herausforderung bei der Entwicklung grafischer Oberflächen besteht darin, diese Komponenten programmatisch zu validieren. Der generierte Code ist nicht einfach nur ein leicht zu überprüfender Wert, sondern bildet einen beliebig tief geschachtelten Baum. Deshalb sind spezielle Werkzeuge notwendig, um die gewünschten Tests durchzuführen.

Facebook und Airbnb haben das Test-Framework Jest und das Testwerkzeug Enzyme speziell für den Komponententest von React-Anwendungen konzipiert. Jest orchestriert und strukturiert Testsuiten und überprüft gewünschte Eigenschaften. Enzyme ergänzt Jest dabei, führt den zu testenden Frontend-Code aus und stellt die zugehörigen Schnittstellen zur Verfügung. Mit diesen Schnittstellen greift man auf die generierten Elemente zu und interagiert mit ihnen.

Dieser Artikel zeigt, wie sich Unit-Tests für React erstellen lassen. Anhand einer Musterkomponente werden React-Grundprinzipien erläutert, sodass sich die Beispiele auch ohne vertiefte React-Kenntnisse nachvollziehen lassen. Diese Komponente, ein simuliertes Thermometer, dient im weiteren Verlauf dieses Artikels außerdem zur Illustration verschiedener Testtechniken und Vorgehensweisen.

Thermometer und Temperatur

Listing 1: Die Thermometerkomponente

import React, { Component } from 'react';
import Temperatur from './Temperatur';

export default class Thermometer extends Component {
  constructor() {
    super();
    this.state = {
      min: Infinity, 
      max: -Infinity
    };
  }

  static getDerivedStateFromProps(props, state) {
    return {
      min: Math.min(props.temp, state.min),
      max: Math.max(props.temp, state.max)
    };
  }

  render() {
    return (
      <div id="thermometer">
        <h1>Thermometer</h1>
        <Temperatur, label="Min" value={this.state.min} />
        <Temperatur, label="Current" value={this.props.temp} />
        <Temperatur, label="Max" value={this.state.max} />
      </div>
    );
  }
}

Der Komponente Thermometer wird über eine Property die aktuelle Temperatur zugewiesen, die sich fortlaufend ändert. Aufgabe der Komponente ist es, die aktuelle Temperatur anzuzeigen sowie über die auftretenden Werte Buch zu führen und zusammen mit der aktuellen die niedrigste und die höchste Temperatur im Messzeitraum auszugeben. Der Konstruktor initialisiert den internen Zustand der Komponente, der die niedrigste und die höchste bislang gemessene Temperatur festhält.

getDerivedStateFromProps ist ein spezieller Hook, also eine Schnittstelle, über die man auf den Lifecycle der Komponente Einfluss nehmen kann. Das Framework ruft diesen Hook immer dann auf, wenn eine Elterninstanz aktualisierte Temperaturwerte übergibt. Sie erhält die neuen Properties als ersten Parameter, der zweite Parameter stellt den aktuellen Zustand dar. Der Rückgabewert der Funktion ist dann der neue State. Im Beispiel des Thermometers gleicht die Komponente die aktuelle Temperatur mit der gespeicherten Minimum- und Maximumtemperatur ab und aktualisiert diese gegebenenfalls.

Die render-Methode stellt die bisher gesammelten Werte im Browser dar. Dabei kommt JSX, ein JavaScript-Superset, zum Einsatz. Beim Build der Anwendung übersetzt ein spezieller Compiler JSX in JavaScript. Im Beispiel rendert die Komponente eine Überschrift mit dem Text „Thermometer“ sowie drei Sektionen mit den drei Temperaturwerten. Sobald eine übergeordnete Komponente einen neuen Temperaturwert übergibt, wird der daraus resultierende state ermittelt und die Komponente neu dargestellt.

Listing 2: Die Temperaturkomponente

class Temperatur extends React.Component {
  render() {
    const {label, value} = this.props;

    return (
      <div id={label.toLowerCase()} className="temp">
        {label}: {this._round(value)}°C
      </div>
    );
  }

  _round(value) {
    return Math.round(value * 100) / 100;
  }
}

React ist stark komponentenbasiert, die einzelnen Aufgaben und Darstellungen sollen in separate Komponenten ausgelagert werden. Dies ist auch beim Thermometer geschehen: Eine separate Komponente stellt den Temperaturwert innerhalb der Thermometerkomponente dar. Diese erhält über Properties das anzuzeigende Label sowie den Temperaturwert (siehe Listing 2), mit round gerundet auf zwei Stellen nach dem Komma. Label und gerundeter Wert stecken in einem div-Element, als dessen ID das in Kleinbuchstaben konvertierte Label dient. Dieses Element enthält das Label sowie den gerundeten Temperaturwert inklusive Einheit. Alle gezeigten Codebeispiele und Tests findet man unter ix.de/ix1902048.