Unit Testing Reaktionskomponenten: Scherz oder Enzym?

Hintergrund

Das Testen von React-Komponenten kann eine schwierige Aufgabe sein, besonders wenn Sie gerade erst damit anfangen. Selbst die grundlegende Aufgabe, zu wissen, mit welchen Tools es getestet werden soll, kann bereits verwirrend sein. Zwei beliebte Optionen zum Testen von React-Komponenten sind Jest und Enzyme. Entwickler, die mit React arbeiten, werden mit der Frage konfrontiert: Was soll ich verwenden, Jest oder Enzyme?

Hier sind nur einige Threads, die diesen Vergleich erwähnt haben oder einen als Alternative für den anderen angeboten haben:

Dieses Tutorial zielt darauf ab, den Rekord klarzustellen: Anstatt zu fragen “Jest oder Enzym”, eine angemessenere Betrachtungsweise sollte “Jest und Enzym!”

Anmerkung des Herausgebers: Aus diesem Grund wurde dieses Tutorial von seiner ursprünglichen Version, die am 20. September 2016 veröffentlicht wurde, neu geschrieben. Diese aktuelle Version wurde aus Gründen der Klarheit bearbeitet.


Einführung

Reagieren ist eine UI-Bibliothek zum Schreiben von Komponenten, und das Testen von Komponenten React-Komponenten ist viel besser organisiert.

Bevor wir über Enzyme und Jest sprechen, sollten wir einige Begriffe definieren: Test Runner, Assertion Library und Mocking Library.

  • Testläufer — ein Tool, das Dateien aufnimmt, die Unit-Tests enthalten, diese ausführt und die Testergebnisse in die Konsolen- oder Protokolldateien schreibt. Mocha und Jasmine sind zwei beliebte Testrunner, die in der JavaScript-Community verwendet werden. Einen Vergleich zwischen den beiden können Sie hier nachlesen.
  • Assertion-Bibliothek — verifiziert die Ergebnisse eines Tests. Chai, Should und Expect sind Beispiele für JavaScript-Assertion-Bibliotheken.
  • Verspottet — Wird beim Komponententest einer Komponente verwendet. Eine zu testende Komponente hat viele Abhängigkeiten. Diese Abhängigkeiten werden normalerweise durch Stubs oder Mocks ersetzt. Stubs simulieren ein abhängiges Objekt. Mocks bieten eine zusätzliche Funktion gegenüber Stubs. Mit Mocks können Tests geschrieben werden, um zu überprüfen, ob die zu testende Komponente die Mocks wie erwartet aufgerufen hat.
  • Verspottende Bibliothek — erleichtert die Verwendung von Mocks in Unit-Tests. Sinon und TestDouble sind häufig verwendete JavaScript-Mocking-Bibliotheken.

Enzym

Aus der Enzymdokumentation:

Enzyme ist ein JavaScript-Testdienstprogramm für React, das es einfacher macht, die Ausgabe Ihrer React-Komponenten zu behaupten, zu manipulieren und zu durchlaufen.

Enzyme ist eine Bibliothek, die Pakete wie React TestUtils, JSDOM und CheerIO umschließt, um eine einfachere Schnittstelle zum Schreiben von Komponententests zu erstellen. React TestUtils verfügt über Methoden, um eine Reaktionskomponente in ein Dokument zu rendern und ein Ereignis zu simulieren. JSDOM ist eine JavaScript-Implementierung des DOM (Document Object Model). DOM repräsentiert die Baumstruktur von UI-Komponenten. CheerIO implementiert eine Teilmenge des jQuery-Kerns und wird zum Abfragen des DOM verwendet. Enzyme umschließt diese Bibliotheken und bietet eine einfache und intuitive API für Unit-Tests.

Enzym ist nicht ein Unit-Testing-Framework. Es hat keinen Test-Runner oder eine Assertion-Bibliothek. Es funktioniert mit jedem Test-Runner und jeder Assertion-Bibliothek. Insofern ist es nicht eigensinnig.

Ist

Zitieren der Jest-Dokumentation:

Jest ist ein Framework zum Testen von JavaScript-Einheiten, das von Facebook zum Testen von Diensten und React-Anwendungen verwendet wird.

Jest ist ein Framework und keine Bibliothek. Es kommt mit einem Test-Runner, einer Assertion-Bibliothek und guter Mocking-Unterstützung. Jest ist darauf aufgebaut Jasmin.

Jest hat eine neuartige Möglichkeit, Reaktionskomponenten zu testen: Snapshot-Tests. Beim Snapshot-Test wird die Ausgabe des aktuellen Testlaufs mit dem Snapshot des vorherigen Testlaufs verglichen. Wenn die Ausgabe mit dem Snapshot übereinstimmt, ist der Test bestanden.

Vergleich von Enzym mit Jest

Wie bereits erwähnt, vergleichen die meisten Entwickler, die Unit-Tests für eine React-Anwendung schreiben wollen, die beiden Alternativen: Enzyme oder Jest. Aber in Wirklichkeit ist dieser Vergleich wie der Vergleich von Äpfeln und Birnen. Enzyme ist eine Testbibliothek, um die Reaktionskomponente in das DOM zu rendern und den DOM-Baum abzufragen. Jest ist ein Unit-Testing-Framework und verfügt über einen Test-Runner, eine Assertion-Bibliothek und Mocking-Unterstützung. Enzyme und Jest ergänzen sich. Enzym kann in Jest verwendet werden.

Es gibt jedoch triftige Gründe, warum Entwickler die beiden vergleichen.

  • Sowohl Enzyme als auch Jest wurden speziell zum Testen von React-Anwendungen entwickelt.
  • Enzym funktioniert nur mit React.
  • Jest ist eher ein Testframework und kann mit Non-React-Anwendungen verwendet werden.
  • Nicht viele Leute verwenden Jest außerhalb von React-Anwendungen. Es gibt also eine Tendenz, die beiden zu vergleichen.

Anstatt die beiden zu vergleichen, zeigt dieses Tutorial, wie man Jest und Enzyme zusammen verwendet, um React-Anwendungen zu testen.

Erste Schritte mit Jest und Enzyme

Der erste Schritt ist die Installation von React.

npm install --save react react-dom

Wir werden eine einfache React-Komponente erstellen, die eine Hallo-Welt-Nachricht anzeigt.

export default class Welcome extends React.Component {
  render() {
    return (
      <div>Hello world</div>
    );
  }
}

Die Welcome-Komponente ist eine React-Komponente, die JSX verwendet. Zum Analysieren von JSX- und ES6-Code sind einige Add-on-Pakete erforderlich.

npm install --save-dev 
babel-core 
babel-loader 
babel-polyfill 
babel-preset-es2015 
babel-preset-react

Außerdem wird in unserem Projekt eine babelrc-Datei benötigt, die das ES2015- und React-Preset verwendet. Der Inhalt der .babelrc-Datei ist unten dargestellt.

{
  "presets": ["es2015", "react"]
}

Als nächstes installieren wir Jest.

npm install --save-dev
jest
babel-jest
react-test-renderer

Jest sucht nach Tests in einem Ordner namens __tests__ . Wir werden Snapshot-Tests mit Jest untersuchen.

import React from 'react';
import renderer from 'react-test-renderer';
import Welcome from '../src/client/components/welcome.jsx';

describe('Welcome (Snapshot)', () => {
  it('Welcome renders hello world', () => {
    const component = renderer.create(<Welcome />);
    const json = component.toJSON();
    expect(json).toMatchSnapshot();
  });
});

Jest benötigt das React-Test-Renderer-Paket, um die Komponente in JSON zu rendern. Das describe Funktion wird verwendet, um eine Testsuite zu schreiben. Das it Funktion wird verwendet, um einen Test zu definieren. Das expect Die Funktion ist Teil der von Jest bereitgestellten Assertion-Bibliothek. Das toMatchSnapshot -Methode vergleicht die JSON-Ausgabe mit dem JSON-Snapshot im vorherigen Testlauf.

Um die Tests auszuführen, geben Sie ein jest in der Kommandozeile. Die Konsolenausgabe beim Ausführen von Jest ist unten dargestellt.
Komponententests reagieren Komponenten

Beim Ausführen des Tests wird eine Snapshot-Datei innerhalb der erstellt __snapshots__ Mappe. Der Schnappschuss für unsere Komponente ist unten dargestellt.

exports[`Welcome (Snapshot) Welcome renders hello world 1`] = `
<div>
  Hello world
</div>
`;

Snapshot-Testing ist eine weitere neue Idee von Facebook. Es bietet eine alternative Möglichkeit, Tests ohne Zusicherungen zu schreiben. Um Tests mit Behauptungen zu schreiben, ist Enzyme sehr nützlich.

Die Installation des Enzyms ist unkompliziert.

npm install --save-dev enzyme

Der Test für die Welcome-Komponente mit Enzyme ist unten dargestellt.

import React from 'react';
import expect from 'expect';
import { shallow } from 'enzyme';
import Welcome from '../src/client/components/welcome.jsx';

describe('Welcome', () => {
  it('Welcome renders hello world', () => {
    const welcome = shallow(<Welcome />);
    expect(welcome.find('div').text()).toEqual('Hello world');
  });
});

Das shallow -Funktion in Enzyme führt ein flaches Rendering für das DOM durch. Beim flachen Rendern werden keine Komponenten gerendert, die in der Welcome-Komponente verschachtelt sind. Das find -Methode in Enzyme akzeptiert jQuery-ähnliche Selektoren, um einen Knoten aus dem DOM-Baum abzurufen. Im obigen Test wird der Text des div-Knotens abgerufen und als gleich geltend gemacht 'Hello world'.
Abgesehen von flachem Rendern ermöglicht Enzyme vollständiges Rendern mit der mount Funktion und HTML-Rendering mit der render Funktion. Flaches Rendering wird verwendet, um die Komponente für Komponententests zu isolieren.

Testen der Struktur

Wir werden eine Listenkomponente erstellen, die eine Liste von Elementen in tabellarischer Form anzeigt. Die zu testende React-Komponente ist unten dargestellt.

export default class List extends React.Component {
  render() {
    return (
      <table className="myClass">
        <thead>
          <tr>
            <th>Name</th>
          </tr>
        </thead>
        <tbody>
          {this.props.data.map((item, index) => (
            <tr key={index}>
              <td>
                {item}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    )
  }
}

Wir werden testen, ob die React-Komponente eine Tabelle rendert und die Daten in Requisiten übergeben.

Unsere Testsuite wird nach der React-Komponente (List) benannt. Wir werden alle Tests in der Testsuite mit a initialisieren beforeEach Funktion. Das beforeEach Funktion verwendet die shallow Funktion zum Rendern der Komponente.

describe('List', () => {
  let list;

  beforeEach(() => {
    list = shallow(<List data={['Name 1', 'Name 2', 'Name 3']} />);
  });

});

Das find -Methode unterstützt jQuery-Selektoren, um bestimmte Knoten aus der Struktur abzurufen. Um zu überprüfen, ob eine Tabelle gerendert wird, verwenden wir die 'table' Wähler. Um zu überprüfen, ob eine bestimmte Klasse gerendert wird, verwenden wir die '.myClass' Wähler.

it('List renders table', () => {
  expect(list.find('table').length).toEqual(1);
});

it('Class of rendered table', () => {
  expect(list.find('.myClass').length).toEqual(1);
});

Das find -Methode gibt ein Array von Knoten zurück. Um das erste Element abzurufen, verwenden wir die first Methode. Um das n-te Element abzurufen, verwenden wir die at Methode.

it('List renders column', () => {
  const arr = list.find('th');
  expect(arr.length).toEqual(1);
  expect(arr.first().text()).toEqual('Name');
});

it('List renders data', () => {
  const arr = list.find('td');
  expect(arr.length).toEqual(3);
  expect(arr.at(1).text()).toEqual('Name 2');
});

Testen des Verhaltens

Wir erstellen eine Add-Komponente, die ein Formularelement mit einem Eingabetext und einer Schaltfläche enthält. Wir wollen das Verhalten der Komponente testen, wenn auf die Schaltfläche geklickt wird. Der Code für Komponente hinzufügen ist unten dargestellt.

export default class Add extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        name: ''
      };
      this.handleAdd = this.handleAdd.bind(this);
    }

    handleAdd(e) {
        e.preventDefault();
        this.props.onAdd(this.state.name);
    }

    render() {
      return (
        <form>
          <input
            type="text"
            name="name"
            value={this.state.name}
            onChange={e => this.setState({ name: e.target.value })}
          >
          </input>
          <button onClick={this.handleAdd}>Add</button>
        </form>
      );
    }
}

Die Add-Komponente ruft die auf handleAdd Methode, wenn auf die Schaltfläche geklickt wird. Das handleAdd Methode ruft die auf onAdd Funktion in den Requisiten festgelegt. Normalerweise wird die eigentliche Arbeit von der übergeordneten Komponente oder einer Redux-Aktion ausgeführt.

Das Testverhalten wird am besten durch die Montage des Bauteils durchgeführt. Jest hat gute spöttische Unterstützung. Wir werden verwenden jest.fn() um eine Scheinfunktion zu erstellen. Wir liefern den Schein an die onAdd Prop unserer Komponente.

describe('Add', () => {
  let add;
  let onAdd;

  beforeEach(() => {
    onAdd = jest.fn();
    add = mount(<Add onAdd={onAdd} />);
  });

});

Wir beginnen mit dem Testen, indem wir prüfen, ob die onAdd Requisite ist definiert. Wir werden auch die Struktur testen, indem wir prüfen, ob die Schaltfläche gerendert wird.

it('Add requires onAdd prop', () => {
  expect(add.props().onAdd).toBeDefined();
});

it('Add renders button', () => {
  const button = add.find('button').first();
  expect(button).toBeDefined();
});

Wir testen dann das Verhalten der Komponente, wenn auf die Schaltfläche geklickt wird. Wenn auf die Schaltfläche geklickt wird, sollte unser Mock mit den entsprechenden Argumenten aufgerufen werden.

it('Button click calls onAdd', () => {
  const button = add.find('button').first();
  const input = add.find('input').first();
  input.simulate('change', { target: { value: 'Name 4' } });
  button.simulate('click');
  expect(onAdd).toBeCalledWith('Name 4');
});

Das Enzym erlaubt es nicht, den Wert des Eingabeelements zu ändern. Stattdessen simulieren wir das Änderungsereignis für den Eingabetext. Danach simulieren wir das Click-Event des Buttons. Das simulate -Methode ist nützlich, um Benutzeraktionen zu simulieren.

Es gibt einen Grund, warum ich das empfohlen habe mount Funktion statt der shallow Funktion. Beim flachen Rendering müssen wir die Ereignisargumente zusammen mit dem simulierten Ereignis übergeben. Im obigen Szenario erfordert die Simulation des Click-Ereignisses die Übergabe von Ereignisargumenten. Das handleAdd -Methode in unserer React-Komponente ruft die eventArgs.preventDefault() Methode. Ohne Übergabe von Ereignisargumenten schlägt der Test fehl.

Beim flachen Rendering können Requisiten auf der Komponente nicht getestet werden. Der Aufruf an ShallowWrapper.props().propName wird zurückkehren undefined . Dies sind Einschränkungen beim flachen Rendern. Diese Probleme könnten in zukünftigen Versionen behoben werden.

Testen der verschachtelten Komponenten

Wir werden die Add- und die List-Komponente, die wir in den letzten Abschnitten erstellt haben, in eine neue Komponente namens App einbetten. Der Code für die App-Komponente ist unten dargestellt.

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list: []
    };
    this.handleAdd = this.handleAdd.bind(this);
  }

  handleAdd(name) {
    const list = this.state.list.slice();
    list.push(name);
    this.setState({ list });
  }

  render() {
    return (
      <div>
        <Add onAdd={this.handleAdd} />
        <List data={this.state.list} />
      </div>
    );
  }
}

Wir werden die App-Komponente auf Struktur und Verhalten testen. Die App-Komponente sollte sowohl die Add- als auch die List-Komponente enthalten. Wenn der onAdd -Ereignis der Add-Komponente ausgelöst wird, erhält die List-Komponente ein neues Datenprop.

Wir gebrauchen shallow Funktion, um die Komponente flach zu rendern. Dadurch werden die verschachtelten Komponenten nicht gerendert.

describe('App', () => {
  let app;

  before(() => {
    app = shallow(<App />);
  });
});

Wir testen die Struktur der App-Komponente mit der find Methode. Der Selektor für die innere Komponente ist der Komponentenname. Der folgende Code testet, ob die Komponente die beiden inneren Komponenten Add und List enthält.

it('App renders nested components', () => {
  expect(app.find('Add').length).toEqual(1);
  expect(app.find('List').length).toEqual(1);
});

Wir wollen Komponenteninteraktionen zwischen verschachtelten Komponenten testen. Wenn der onAdd -Ereignis innerhalb der Add-Komponente ausgelöst wird, sollte die List-Komponente mit einem neuen Eintrag aktualisiert werden.

it('onAdd updates List', () => {
  const add = app.find('Add').first();
  add.props().onAdd('Name 1');
  app.update();     
  const list = app.find('List').first();
  const listData = list.props().data;
  expect(listData.length).toEqual(1);
  expect(listData[0]).toEqual('Name 1');
});

Wir erhalten die Add-Komponente mit der find Methode. Wir lösen die aus onAdd Veranstaltung. Wir erlauben der Komponente, sich selbst zu aktualisieren. Nach der Aktualisierung hat die Listenkomponente den neu hinzugefügten Eintrag.

API

Die von offengelegte API Ist und Enzym sind sehr verschieden.

Es gibt eine API

Die Jest-API konzentriert sich mehr auf die Fähigkeit, Tests zu definieren, Behauptungen aufzustellen und Mocks zu erstellen.

  • describe: definiert eine Testsuite.
  • it: definiert einen Test.
  • beforeEach: Definiert einen Entry-Hook, bevor jeder Test ausgeführt wird.
  • expect: stellt eine Behauptung auf.
  • jest.fn(): Erstellt eine Scheinfunktion.

Für Behauptungen stehen mehrere Methoden zur Verfügung.

  • toEqual: prüft, ob zwei Objekte den gleichen Wert haben.
  • toBe: prüft, ob zwei Objekte den gleichen Wert und Typ haben.
  • toBeDefined: prüft, ob das Objekt definiert ist.
  • toContain: überprüft, ob ein Element in einer Liste vorhanden ist.
  • toBeCalled: prüft, ob eine Mock-Funktion aufgerufen wird.

Es gibt nützliche Methoden auf dem Schein.

  • mockImplementation stellt eine Implementierung für die Scheinfunktion bereit.
  • mockReturnValue gibt einen Wert zurück, wenn die Scheinfunktion aufgerufen wird.

Enzym-API

Die Enzym-API konzentriert sich auf das Rendern der Reaktionskomponente und das Abrufen bestimmter Knoten aus dem gerenderten Baum. Es gibt drei Möglichkeiten, eine Komponente zu rendern.

  • shallow: Rendert nur die getestete Komponente. Abhängige Komponenten werden nicht gerendert.
  • mount: Mountet die vollständige Komponente in JSDOM.
  • render: rendert die Komponente als statisches HTML.

Es gibt mehrere Methoden zum Abrufen von Knoten aus der gerenderten Komponente.

  • find: Akzeptiert einen Selektor und ruft Knoten ab, die mit dem Selektor übereinstimmen.
  • findWhere: vom Prädikat ausgewählte Knoten abrufen.
  • some: gibt true zurück, wenn es mindestens einen Knoten gibt, der mit dem Selektor übereinstimmt.
  • someWhere: gibt true zurück, wenn mindestens ein Knoten durch das Prädikat ausgewählt wurde.
  • first: gibt den ersten Knoten einer Menge zurück.
  • at: gibt den n-ten Knoten einer Menge zurück.
  • html: Ruft den HTML-Code des Knotens ab.
  • text: Ruft die Textdarstellung des Knotens ab.

Es gibt weitere Methoden, um mit der Komponente zu interagieren und den Komponentenstatus abzurufen.

  • simulate: simuliert ein Ereignis.
  • setProps: setzt die Requisiten.
  • setState: setzt den Zustand.
  • setContext: legt den Kontext fest.
  • prop(key): Ruft den Prop-Wert ab, der dem bereitgestellten Schlüssel entspricht.
  • state(key): Ruft den Zustand ab, der dem bereitgestellten Schlüssel entspricht.
  • context(key): Ruft den Kontextwert ab, der dem bereitgestellten Schlüssel entspricht.

Fazit

Wir haben in diesem Tutorial gesehen, wie man Enzyme mit Jest verwendet. Enzyme hat eine einfache API zum Testen von React-Komponenten. Jest ist ein von Facebook entwickeltes Unit-Test-Framework zum Testen von Reaktionsanwendungen. Jest ermöglicht das Schreiben von Unit-Tests mit der Snapshot-Funktion. Enzym ermöglicht das Schreiben von Unit-Tests mit regulären Assertionen. Die Verwendung von Enzyme mit Jest macht das Schreiben von Tests für React-Anwendungen viel einfacher.

Es gibt eine Begleitung github-Projekt die den Quellcode enthält, der in diesem Tutorial erklärt wird.

Andere nützliche Ressourcen

Wenn Sie nach diesem Tutorial immer noch zwischen den beiden verwirrt sind, finden Sie hier weitere Ressourcen im Internet, die Ihnen helfen können:

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *