Testowanie aplikacji React.js — podstawy Enzyme

Ten wpis jest 14 częścią z 40 w kursie React.js
wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. React.js: Wprowadzenie do kursu od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux: globalny store i jednokierunkowy przepływ danych
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
  40. Wady React Hooks

Testowanie aplikacji to rzecz ważna. Do tej pory jednak nie wspomniałem ani słowem o testowaniu React.js. Czas najwyższy to zrobić! I od razu wrzucam Cię na głęboką wodę — użyjesz React.js i Enzyme — przemiłej biblioteki do testowania komponentów.

O zaletach samego testowania nie muszę chyba pisać. Utrzymanie kodu, łatwiejsze dodawanie nowych funkcji, testy służące jako dokumentacja… bajka 😉 Dlatego teraz po prostu weźmiesz poprzedni przykład (filtrowanie listy) i napiszesz do niego testy jednostkowe. Zacznij od zainstalowania enzyme.

create-react-app domyślnie korzysta z biblioteki jest do testów. Tak też będę robił w tym wpisie. Ale pamiętaj, że enzyme działa również z innymi popularnymi bibliotekami np. mocha czy chai.

Enzyme

Enzyme to biblioteka do testowania komponentów React.js. Ułatwia tworzenie komponentów na potrzeby testów, odczytywanie oraz zmianę propsów i state, a także pozwala na asercje. Zgodnie z dokumentacją create-react-app aby zacząć używać cra razem z enzyme trzeba zainstalować:

npm install --save enzyme enzyme-adapter-react-16 react-test-renderer

Co robią poszczególne paczki?

  • enzyme — wspomniana biblioteka
  • enzyme-adapter-react-16 — enzyme wymaga zainstalowania odpowiedniego adaptera do konkretnej wersji React.js
  • react-test-renderer — renderowanie bez konieczności istnienia DOM

Następnie stwórz jeszcze jeden plik src/setupTests.js. Tam zawrzyj konfigurację wszystkich testów. W najprostszej wersji wygląda ona tak:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

W tym samym pliku możesz też dodać np. globalne mocki — jeśli Ci będą potrzebne. Albo biblioteki, z których chcesz korzystać w testach.

Pierwszy test w Enzyme

Najprostszy test z użyciem Enzyme będzie po prostu renderował komponent. Jeśli wszystko zadziała poprawnie — test zostanie zaliczony. Jeśli wystąpi wyjątek — test zakończy się niepowodzeniem. Zacznij od zaimportowania React, i App. Do tego potrzebna będzie Ci funkcja shallow z enzyme. Dlaczego akurat shallow? Kilka słów o tym za moment, a na razie test:

import React from 'react';
import { shallow } from 'enzyme';
import App from './App';

it('renders without crashing', () => {
  shallow(<App />);
});

W kodzie powyżej tworzony jest jeden test, który tylko renderuje komponent App. Proste, prawda? 🙂

Odpal teraz polecenie npm test. Twoje testy będą teraz automatycznie uruchamiane przy każdej zmianie w kodzie.

shallow, mount, render

W poprzednim teście skorzystałem z funkcji shallow. Ale są też inne: mount oraz render.

  • shallow — renderuje tylko ten komponent (bez komponentów w nim zagnieżdżonych). Idealny do testów jednostkowych, bo masz pewność, że przypadkiem nie sprawdzasz zachowań innych komponentów w środku. Szybki.
  • mount — komponent jest renderowany i montowany w DOM. Do testowania komponentów, które wchodzą w interakcje z DOM albo są udekorowane przez High Order Components
  • render — renderuje komponent do statycznego HTML-a

Najczęściej w testach jednostkowych będziesz korzystać z shallow.

Prawdziwy test

Dodaj dwa nowe testy. Sprawdź czy App zawiera input oraz czy App zawiera w sobie UsersList:

it('includes input', () => {
  const app = shallow(<App />);
  expect(app.containsMatchingElement(<input />)).toEqual(true)
});

it('includes UsersList', () => {
  const app = shallow(<App />);
  expect(app.containsMatchingElement(<UsersList />)).toEqual(true)
});

Wszystko przechodzi. Ale skąd tak naprawdę masz pewność, że Twój kod działa? Może to błąd w testach i one przechodzą zawsze? 😉 Spróbuj usunąć z komponentu App element input. Oto rezultat:

 FAIL  src/App.test.js
  ● includes input

    expect(received).toEqual(expected)
    
    Expected value to equal:
      true
    Received:
      false

Rzeczywiście, test pokazał, że w komponencie nie ma inputa! Czyli testy są poprawne 😉

Testy przekazywanych propsów

Teraz przetestuj komponent UsersList, którego wyświetlanie zależy od przekazanych propsów:

  1. Jeśli przekazano pustą tablicę to sprawdź czy wyświetla się komunikat, że nic nie znaleziono.
  2. Jeśli przekazano tablicę z imionami to sprawdź czy komunikatu już nie ma.
  3. Sprawdź czy dla każdego imienia jest wyrenderowany element na liście.

Nic prostszego 😉

it('shows message when there are no users', () => {
    const usersList = shallow(<UsersList users={[]} />);
    expect(usersList.text()).toContain('No results!')
});

it(`doesn't show message when there are users`, () => {
    const usersList = shallow(<UsersList users={['Michal']} />);
    expect(usersList.text()).not.toContain('No results!')
});

it(`shows a list of users`, () => {
    const users = ['Michal', 'Ania'];
    const usersList = shallow(<UsersList users={users} />);
    expect(usersList.find('li').length).toEqual(users.length);
});

describe('list of users', () => {
    const users = ['Michal', 'Ania'];
    const usersList = shallow(<UsersList users={users} />);
    
    users.forEach(user => {
        it(`includes name ${user} on the list`, () => {
            expect(usersList.containsMatchingElement(<li>{user}</li>)).toEqual(true)
        });
    });
});

Wszystkie napisane wyżej testy powinny bez problemu przechodzić 🙂

Testy React.js w Enzyme

Podsumowanie

W tej części zrobiłem tylko lekkie wprowadzenie do podstaw enzyme. Cały kod jest dostępny na moim GitHubie: https://github.com/mmiszy/typeofweb-kurs-react/tree/part-2 Naucz się React na szkoleniu Type of Web! W kolejnym wpisie omówię jak testować zmiany propsów i stanu, a także jak przetestować interakcje z komponentami!

Jeśli chcesz na bieżąco dowiadywać się o kolejnych częściach kursu React.js to koniecznie śledź mnie na Facebooku i zapisz się na newsletter.

Nie wysyłamy spamu, tylko wartościowe informacje. W każdej chwili możesz się wypisać klikając „wypisz się” w stopce maila.

Ćwiczenie

Ćwiczenie*:  Przetestuj czy komponent App przekazuje do komponentu UsersList tablicę z imionami. Kod dodaj w komentarzu!

Nawigacja po kursie:
  1. React.js: Wprowadzenie do kursu od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux: globalny store i jednokierunkowy przepływ danych
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
  40. Wady React Hooks

Nie wysyłamy spamu, tylko wartościowe informacje. W każdej chwili możesz się wypisać klikając „wypisz się” w stopce maila.