Unit-Tests in React-nativen Anwendungen

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Unit Testing ist zu einem festen Bestandteil des Softwareentwicklungsprozesses geworden. Es ist die Testebene, auf der die Komponenten der Software getestet werden. In diesem Tutorial erfahren Sie, wie Sie Einheiten einer React Native-Anwendung testen.

React Native ist eines der am häufigsten verwendeten Frameworks zum Erstellen mobiler Anwendungen. Dieses Tutorial richtet sich an Entwickler, die mit dem Testen der von ihnen erstellten React Native-Anwendungen beginnen möchten. Wir werden das Jest-Testframework und Enzyme verwenden.

In diesem Artikel lernen wir die Grundprinzipien des Testens kennen, untersuchen verschiedene Bibliotheken zum Testen einer Anwendung und sehen, wie Einheiten (oder Komponenten) einer React Native-Anwendung getestet werden. Durch die Arbeit mit einer React Native-Anwendung festigen wir unser Testwissen.

Hinweis: Grundkenntnisse in JavaScript und React Native wären beim Durcharbeiten dieses Tutorials von großem Vorteil.

Was ist Unit-Testing?

Unit-Tests sind die Testebene, auf der einzelne Komponenten der Software getestet werden. Wir tun dies, um sicherzustellen, dass jede Komponente wie erwartet funktioniert. Eine Komponente ist der kleinste testbare Teil der Software.

Lassen Sie uns zur Veranschaulichung eine Button -Komponente erstellen und einen Komponententest simulieren:

 import React from 'react'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'; function AppButton({ onPress }) { return ( <TouchableOpacity style={[styles.button, { backgroundColor: colors[color] }]} onPress={onPress} > <Text style={styles.text}>Register</Text> </TouchableOpacity> ); } const styles = StyleSheet.create({ button: { backgroundColor: red; borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, text: { color: #fff } }) export default AppButton;

Diese Button -Komponente hat Text und eine onPress -Funktion. Lassen Sie uns diese Komponente testen, um zu sehen, worum es bei Komponententests geht.

Lassen Sie uns zunächst eine Testdatei mit dem Namen Button.test.js :

 it('renders correctly across screens', () => { const tree = renderer.create(<Button />).toJSON(); expect(tree).toMatchSnapshot(); });

Hier testen wir, ob unsere Button -Komponente auf allen Bildschirmen der Anwendung so dargestellt wird, wie sie sollte. Darum geht es bei Unit-Tests: Testen von Komponenten einer Anwendung, um sicherzustellen, dass sie so funktionieren, wie sie sollten.

Unit-Tests in React-nativen Anwendungen

Eine React Native-Anwendung kann mit einer Vielzahl von Tools getestet werden, von denen einige die folgenden sind:

  • WebDriver
    Dieses Open-Source-Testtool für Node.js-Apps wird auch zum Testen von React Native-Anwendungen verwendet.
  • Albtraum
    Dadurch wird der Testbetrieb im Browser automatisiert. Der Dokumentation zufolge „ist das Ziel, ein paar einfache Methoden bereitzustellen, die Benutzeraktionen nachahmen (wie goto , type und click ), mit einer API, die sich für jeden Skriptblock synchron anfühlt, anstatt tief verschachtelte Rückrufe.“
  • Scherz
    Dies ist eine der beliebtesten Testbibliotheken, auf die wir uns heute konzentrieren werden. Wie React wird es von Facebook verwaltet und wurde entwickelt, um ein „Zero Config“-Setup für maximale Leistung bereitzustellen.
  • Mokka
    Mocha ist eine beliebte Bibliothek zum Testen von React- und React Native-Anwendungen. Es ist zu einem Testwerkzeug der Wahl für Entwickler geworden, da es einfach einzurichten und zu verwenden ist und wie schnell es ist.
  • Jasmin
    Laut seiner Dokumentation ist Jasmine ein verhaltensgesteuertes Entwicklungsframework zum Testen von JavaScript-Code.
Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Einführung in Jest und Enzym

Laut seiner Dokumentation ist „Jest ein entzückendes JavaScript-Testframework mit Fokus auf Einfachheit“. Es funktioniert ohne Konfiguration. Nach der Installation (mithilfe eines Paketmanagers wie npm oder Yarn) ist Jest einsatzbereit, ohne dass weitere Installationen erforderlich sind.

Enzyme ist ein JavaScript-Testframework für React-Native-Anwendungen. (Wenn Sie mit React statt React Native arbeiten, ist eine Anleitung verfügbar.) Wir verwenden Enzyme, um Einheiten der Ausgabe unserer Anwendung zu testen. Damit können wir die Laufzeit der Anwendung simulieren.

Beginnen wir mit der Einrichtung unseres Projekts. Wir verwenden die Done With It-App auf GitHub. Es ist ein Marktplatz für React Native-Anwendungen. Beginnen Sie mit dem Klonen, navigieren Sie in den Ordner und installieren Sie die Pakete, indem Sie Folgendes für npm ausführen …

 npm install

… oder das für Garn:

 yarn install

Dieser Befehl installiert alle Pakete in unserer Anwendung. Sobald dies erledigt ist, testen wir die UI-Konsistenz unserer Anwendung mithilfe von Snapshots, die unten behandelt werden.

Snapshots und Scherzkonfiguration

In diesem Abschnitt testen wir Benutzereingaben und die Benutzeroberfläche der App-Komponenten, indem wir Snapshots mit Jest testen.

Zuvor müssen wir Jest und seine Abhängigkeiten installieren. Führen Sie den folgenden Befehl aus, um Jest für Expo React Native zu installieren:

 yarn add jest-expo --dev

Dadurch wird jest-expo im Verzeichnis unserer Anwendung installiert. Als nächstes müssen wir unsere package.json -Datei aktualisieren, um ein Testskript zu haben:

 "scripts": { "test" "jest" }, "jest": { "preset": "jest-expo" }

Indem wir diesen Befehl hinzufügen, teilen wir Jest mit, welches Paket in unserer Anwendung registriert werden soll und wo.

Als nächstes fügen wir weitere Pakete zu unserer Anwendung hinzu, die Jest dabei helfen, einen umfassenden Test durchzuführen. Führen Sie für npm Folgendes aus…

 npm i react-test-renderer --save-dev

… und für Garn dies:

 yarn add react-test-renderer --dev

Wir müssen noch eine kleine Konfiguration in unserer Datei package.json . Gemäß der Dokumentation von Expo React Native müssen wir eine transformIgnorePattern -Konfiguration hinzufügen, die verhindert, dass Tests in Jest ausgeführt werden, wenn eine Quelldatei mit einem Test übereinstimmt (dh wenn ein Test durchgeführt wird und eine ähnliche Datei in den node modules des Projekts gefunden wird).

 "jest": { "preset": "jest-expo", "transformIgnorePatterns": [ "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)" ] }

Lassen Sie uns nun eine neue Datei mit dem Namen App.test.js , um unseren ersten Test zu schreiben. Wir werden testen, ob unsere App ein untergeordnetes Element in ihrem Baum hat:

 import React from "react"; import renderer from "react-test-renderer"; import App from "./App.js" describe("<App />", () => { it('has 1 child', () => { const tree = renderer.create(<App />).toJSON(); expect(tree.children.length).toBe(1); }); });

Führen Sie jetzt den yarn test oder sein npm-Äquivalent aus. Wenn App.js ein einzelnes untergeordnetes Element hat, sollte unser Test bestanden werden, was in der Befehlszeilenschnittstelle bestätigt wird.

Im obigen Code haben wir React und React react-test-renderer importiert, wodurch unsere Tests für Expo werden. Wir haben die <App /> -Komponentenstruktur in JSON konvertiert und dann Jest gebeten, zu prüfen, ob die zurückgegebene Anzahl untergeordneter Komponenten in JSON unseren Erwartungen entspricht.

Weitere Snapshot-Tests

Wie David Adeneye sagt:

„Ein Snapshot-Test stellt sicher, dass sich die Benutzeroberfläche (UI) einer Webanwendung nicht unerwartet ändert. Es erfasst den Code einer Komponente zu einem bestimmten Zeitpunkt, sodass wir die Komponente in einem Zustand mit jedem anderen möglichen Zustand vergleichen können, den sie einnehmen könnte.“

Dies geschieht insbesondere dann, wenn ein Projekt globale Stile umfasst, die für viele Komponenten verwendet werden. Lassen Sie uns einen Snapshot-Test für App.js schreiben, um seine UI-Konsistenz zu testen:

 it('renders correctly across screens', () => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(); }); it('renders correctly across screens', () => { const tree = renderer.create( ).toJSON(); expect(tree).toMatchSnapshot(); });

Fügen Sie dies zu den Tests hinzu, die wir bereits geschrieben haben, und führen Sie dann den yarn test (oder sein npm-Äquivalent) aus. Wenn unser Test bestanden wird, sollten wir Folgendes sehen:

 PASS src/App.test.js √ has 1 child (16ms) √ renders correctly (16ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 1 total Time: 24s

Dies sagt uns, dass unsere Tests bestanden wurden und wie lange sie gedauert haben. Ihr Ergebnis wird ähnlich aussehen, wenn die Tests bestanden wurden.

Fahren wir fort, einige Funktionen in Jest zu verspotten.

Verspotten von API-Aufrufen

Laut Jests Dokumentation:

Mit Mock-Funktionen können Sie die Verknüpfungen zwischen Code testen, indem Sie die tatsächliche Implementierung einer Funktion löschen, Aufrufe an die Funktion (und die in diesen Aufrufen übergebenen Parameter) erfassen, Instanzen von Konstruktorfunktionen erfassen, wenn sie mit "new" instanziiert werden, und Test- Zeitkonfiguration der Rückgabewerte.

Einfach ausgedrückt ist ein Mock eine Kopie eines Objekts oder einer Funktion ohne die tatsächliche Funktionsweise dieser Funktion. Es ahmt diese Funktion nach.

Mocks helfen uns auf so viele Arten beim Testen von Apps, aber der Hauptvorteil besteht darin, dass sie unseren Bedarf an Abhängigkeiten reduzieren.

Mocks können normalerweise auf zwei Arten ausgeführt werden. Eine besteht darin, eine Mock-Funktion zu erstellen, die in den zu testenden Code eingefügt wird. Die andere besteht darin, eine Scheinfunktion zu schreiben, die das Paket oder die Abhängigkeit überschreibt, die an die Komponente angehängt ist.

Die meisten Organisationen und Entwickler ziehen es vor, manuelle Mocks zu schreiben, die die Funktionalität imitieren und gefälschte Daten verwenden, um einige Komponenten zu testen.

React Native schließt fetch in das globale Objekt ein. Um echte API-Aufrufe in unserem Komponententest zu vermeiden, verspotten wir sie. Unten finden Sie eine Möglichkeit, alle, wenn nicht die meisten unserer API-Aufrufe in React Native zu verspotten, und zwar ohne die Notwendigkeit von Abhängigkeiten:

 global.fetch = jest.fn(); // mocking an API success response once fetch.mockResponseIsSuccess = (body) => { fetch.mockImplementationForOnce ( () => Promise.resolve({json: () => Promise.resolve(JSON.parse(body))}) ); }; // mocking an API failure response for once fetch.mockResponseIsFailure = (error) => { fetch.mockImplementationForOnce( () => Promise.reject(error) ); };

Hier haben wir eine Funktion geschrieben, die versucht, eine API einmal abzurufen. Danach gibt es ein Versprechen zurück, und wenn es aufgelöst ist, gibt es den Körper in JSON zurück. Sie ähnelt der Scheinantwort für eine fehlgeschlagene Abruftransaktion – sie gibt einen Fehler zurück.

Unten ist die product unserer Anwendung, die ein product enthält und die Informationen als props .

 import React from 'react'; const Product = () => { const product = { name: 'Pizza', quantity: 5, price: '$50' } return ( <> <h1>Name: {product.name}</h1> <h1>Quantity: {product.quantity}</h1> <h1>Price: {product.price}</h1> </> ); } export default Product;

Stellen wir uns vor, wir versuchen, alle Komponenten unseres Produkts zu testen. Ein direkter Zugriff auf unsere Datenbank ist keine praktikable Lösung. Hier kommen Mocks ins Spiel. Im folgenden Code versuchen wir, eine Komponente des Produkts zu simulieren, indem wir Jest verwenden, um die Objekte in der Komponente zu beschreiben.

 describe("", () => { it("accepts products props", () => { const wrapper = mount(<Customer product={product} />); expect(wrapper.props().product).toEqual(product); }); it("contains products quantity", () => { expect(value).toBe(3); }); });

Wir verwenden describe von Jest, um die Tests zu diktieren, die wir durchführen möchten. Im ersten Test prüfen wir, ob das Objekt, an dem wir vorbeikommen, den von uns verspotteten Requisiten entspricht.

Im zweiten Test bestehen wir die customer , um sicherzustellen, dass es sich um ein Produkt handelt und dass es zu unseren Mocks passt. Auf diese Weise müssen wir nicht alle Komponenten unseres Produkts testen, und wir können auch Fehlern in unserem Code vorbeugen.

Verspotten externer API-Anfragen

Bisher haben wir Tests für API-Aufrufe mit anderen Elementen in unserer Anwendung durchgeführt. Lassen Sie uns nun einen externen API-Aufruf simulieren. Wir werden Axios verwenden. Um einen externen Aufruf an eine API zu testen, müssen wir unsere Anfragen simulieren und auch die Antworten verwalten, die wir erhalten. Wir werden axios-mock-adapter verwenden, um Axios zu verspotten. Zuerst müssen wir axios-mock-adapter installieren, indem wir den folgenden Befehl ausführen:

 yarn add axios-mock-adapter

Als nächstes müssen Sie unsere Mocks erstellen:

 import MockAdapter from 'axios-mock-adapter'; import Faker from 'faker' import ApiClient from '../constants/api-client'; import userDetails from 'jest/mockResponseObjects/user-objects'; let mockApi = new MockAdapter(ApiClient.getAxiosInstance()); let validAuthentication = { name: Faker.internet.email(), password: Faker.internet.password() mockApi.onPost('requests').reply(config) => { if (config.data === validAuthentication) { return [200, userDetails]; } return [400, 'Incorrect username and password']; });

Hier rufen wir den ApiClient und übergeben ihm eine Axios-Instanz, um die Anmeldeinformationen des Benutzers zu simulieren. Wir verwenden ein Paket namens faker.js, um gefälschte Benutzerdaten wie E-Mail-Adresse und Passwort zu generieren.

Der Mock verhält sich so, wie wir es von der API erwarten. Wenn die Anfrage erfolgreich ist, erhalten wir eine Antwort mit dem Statuscode 200 für OK. Und wir erhalten einen Statuscode von 400 für eine fehlerhafte Anfrage an den Server, die mit JSON mit der Meldung „Falscher Benutzername und Passwort“ gesendet würde.

Nachdem unser Mock nun fertig ist, schreiben wir einen Test für eine externe API-Anforderung. Wie zuvor werden wir Snapshots verwenden.

 it('successful sign in with correct credentials', async () => { await store.dispatch(authenticateUser('[email protected]', 'password')); expect(getActions()).toMatchSnapshot(); }); it('unsuccessful sign in with wrong credentials', async () => { await store.dispatch(authenticateUser('[email protected]', 'wrong credential')) .catch((error) => { expect(errorObject).toMatchSnapshot(); });

Hier testen wir auf eine erfolgreiche Anmeldung mit den richtigen Anmeldeinformationen, wobei wir das native JavaScript async await , um unsere Eingaben zu speichern. In der Zwischenzeit authentifiziert die authenticateUser -Funktion von Jest die Anfrage und stellt sicher, dass sie mit unseren früheren Snapshots übereinstimmt. Als nächstes testen wir auf eine erfolglose Anmeldung im Falle falscher Anmeldeinformationen wie E-Mail-Adresse oder Passwort und senden eine Fehlermeldung als Antwort.

Führen Sie jetzt yarn test oder npm test aus. Ich bin sicher, alle deine Tests werden bestehen.

Sehen wir uns an, wie Komponenten in einer Zustandsverwaltungsbibliothek, Redux, getestet werden.

Testen von Redux-Aktionen und Reducern mit Snapshots

Es lässt sich nicht leugnen, dass Redux einer der am häufigsten verwendeten Zustandsmanager für React-Anwendungen ist. Die meisten Funktionen in Redux beinhalten eine dispatch -Funktion, die eine Funktion des Redux-Speichers ist, die verwendet wird, um eine Statusänderung einer Anwendung auszulösen. Das Testen von Redux kann schwierig sein, da die actions von Redux schnell an Größe und Komplexität zunehmen. Mit Jest-Snapshots wird dies einfacher. Die meisten Tests mit Redux laufen auf zwei Dinge hinaus:

  • Um actions zu testen, erstellen wir redux-mock-store und versenden die Aktionen.
  • Um Reduzierer zu testen, importieren wir den reducer und übergeben ihm ein Zustands- und Aktionsobjekt.

Unten ist ein Redux-Test mit Schnappschüssen. Wir werden die Aktionen testen, die durch die Authentifizierung des Benutzers beim SIGN-IN gesendet werden, und sehen, wie die LOGOUT -Aktion vom user gehandhabt wird.

 import mockStore from 'redux-mock-store'; import { LOGOUT } from '../actions/logout'; import User from '../reducers/user'; import { testUser } from 'jest/mock-objects'; describe('Testing the sign in authentication', () => { const store = mockStore(); it('user attempts with correct password and succeeds', async () => { await store.dispatch(authenticateUser('[email protected]', 'password')); expect(store.getActions()).toMatchSnapshot(); }); }); describe('Testing reducers after user LOGS OUT', () => { it('user is returned back to initial app state', () => { expect(user(testUser, { type: LOGOUT })).toMatchSnapshot(); }); });

Im ersten Test beschreiben wir die Anmeldeauthentifizierung und erstellen einen Scheinspeicher. Wir tun dies, indem wir zuerst einen mockStore von Redux importieren und dann eine Methode namens testUser von Jest importieren, um uns dabei zu helfen, einen Benutzer zu verspotten. Als Nächstes testen wir, wann sich der Benutzer mit einer E-Mail-Adresse und einem Passwort, die mit denen in unserem Snapshot-Speicher übereinstimmen, erfolgreich bei der Anwendung anmeldet. Der Snapshot stellt also sicher, dass die Objekte, die der Benutzer eingibt, bei jeder Ausführung eines Tests übereinstimmen.

Im zweiten Test testen wir, wann sich der Benutzer abmeldet. Sobald unser Reducer-Snapshot bestätigt, dass sich ein Benutzer abgemeldet hat, kehrt er zum Ausgangszustand der Anwendung zurück.

Als nächstes testen wir, indem wir den yarn test . Wenn die Tests bestanden wurden, sollten wir das folgende Ergebnis sehen:

 PASS src/redux/actions.test.js √ user attempts with correct password and succeeds (23ms) √ user is returned back to initial app state (19ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 2 total Time: 31s

Fazit

Mit Jest war das Testen von React Native-Anwendungen noch nie einfacher, insbesondere mit Snapshots, die sicherstellen, dass die Benutzeroberfläche unabhängig von den globalen Stilen konsistent bleibt. Außerdem ermöglicht uns Jest, bestimmte API-Aufrufe und Module in unserer Anwendung zu simulieren. Wir können dies weiterführen, indem wir Komponenten einer React Native-Anwendung testen.

Weitere Ressourcen

  • „Ein praktischer Leitfaden zum Testen von React-nativen Anwendungen mit Jest“, David Adeneye, Smashing Magazine
  • Witzige Dokumentation
  • „Testing With Jest“, Expo React Native-Dokumentation
  • „Lernen, Reagieren mit Spaß zu testen“, Jason Gaare