Testowanie komponentów w React: co i jak testować z Jest i Enzymem.

Artykuł na temat testowania komponentów reagujących napisał Alona Pysarenko - inżynier frontendu w Django Stars.
Przeczytaj oryginalny artykuł na blogu Django Stars.

Testowanie komponentów React może stanowić wyzwanie zarówno dla początkujących, jak i doświadczonych programistów, którzy już pracowali z testami. Interesujące może być porównanie własnych podejść z tymi, które wykorzystujemy w naszym projekcie. Aby pokryć bazę kodu, musisz wiedzieć, które komponenty należy przetestować i który kod dokładnie w komponencie powinien zostać objęty.

W tym artykule omówię następujące tematy:

  • Określ prawidłową kolejność testowania komponentów na podstawie struktury projektu
  • Znajdź, co należy pominąć w zasięgu testu (czego nie testować)
  • Określ konieczność testowania migawek
  • Zdefiniuj, co ma być testowane w komponencie iw jakiej kolejności
  • Podaj szczegółowe przykłady niestandardowego kodu

Artykuł wymaga znajomości konfiguracji Jest i enzymu. Informacje na temat instalacji i konfiguracji można łatwo znaleźć na ich oficjalnych stronach internetowych.

Załóżmy następujący przypadek: Musisz pokryć bazę kodu projektu testami. Od czego powinieneś zacząć i co powinieneś dostać pod koniec testowania? 100% pokrycia testowego? Jest to punkt odniesienia, do którego powinieneś dążyć, ale w większości sytuacji go nie rozumiesz.

Dlaczego? Ponieważ nie powinieneś testować całego kodu. Dowiemy się, dlaczego i co należy pominąć w testach. Co więcej, 100% pokrycia testowego nie zawsze zapewnia pełne przetestowanie komponentu. Nie ma również gwarancji, że powiadomi Cię, jeśli coś zostało zmienione. Nie staraj się uzyskać wartości procentowych, unikaj pisania fałszywych testów i po prostu staraj się nie zgubić szczegółów głównego elementu.

Określenie prawidłowej kolejności testowania komponentów na podstawie struktury projektu

Omówmy to pytanie w następnej części struktury projektu:

Wziąłem udostępniony katalog, ponieważ jest on najważniejszy. Składa się z komponentów używanych na kilku różnych stronach projektu. Są wielokrotnego użytku i zwykle są małe i nieskomplikowane. Jeśli jeden lub inny komponent ulegnie awarii, spowoduje awarię w innych miejscach. Dlatego powinniśmy mieć pewność, że zostały poprawnie napisane. Struktura tego katalogu jest podzielona na kilka folderów, z których każdy zawiera składniki.

Jak zdefiniować prawidłową kolejność testowania komponentów we współdzielonym katalogu:

  • Zawsze postępuj zgodnie z zasadą przejścia od prostej do złożonej. Przeanalizuj każdy katalog i zdefiniuj, które komponenty są niezależne - a mianowicie ich renderowanie nie zależy od innych komponentów. Są one samodzielnie uzupełniane i mogą być używane osobno jako jedna jednostka. Z powyższej struktury jest to katalog wejściowy w folderze formularzy. Zawiera elementy wejściowe do formularzy redux, takich jak TextInput, SelectInput, CheckboxInput, DateInput itp.
  • Następnie musimy zdefiniować komponenty pomocnicze, które są często używane w komponentach wejściowych, ale należy je przetestować niezależnie od nich. To jest katalog utils. Składniki w tym folderze nie są skomplikowane, ale bardzo ważne. Często są wielokrotnego użytku i pomagają w powtarzaniu czynności.
  • Następnym krokiem jest określenie, które komponenty mogą być używane niezależnie. Jeśli tak, zabierz je do testowania. Z naszej struktury są to widżety, małe komponenty o prostej funkcjonalności. Będą trzecią pozycją w kolejce do pokrycia testowego.
  • Następnie przeanalizuj pozostałe katalogi i zdefiniuj bardziej złożone komponenty, które mogą być używane niezależnie lub w połączeniu z innymi komponentami. W naszym przypadku jest to katalog modałów. Elementy te zostaną szczegółowo wyjaśnione poniżej.
  • Najbardziej złożone komponenty są pozostawione do końca. Są to katalog hoc i pola z folderu formularzy. Jak zdefiniujesz, który z nich powinien zostać przetestowany jako pierwszy? Pobieram katalog, z którego komponenty zostały już użyte w testowanych komponentach. Zatem komponent z katalogu hoc był obecny w komponencie widgetów. Dlatego już wiem, gdzie i do czego służy ten katalog i jego komponent.
  • Ostatni to folder pól. Zawiera komponenty związane z formularzami redux.

Ostateczna kolejność komponentów (na podstawie naszego przykładu) będzie wyglądać następująco:

Postępując w tej kolejności, krok po kroku zwiększasz złożoność testowanych komponentów. Tak więc, jeśli chodzi o pracę z bardziej złożonymi komponentami, wiesz już, jak zachowują się te najmniejsze.

Nie bierz do testowania, na przykład, pola „tablica”, jeśli nie masz pewności, jak przetestować pole „tekst”. Nie bierz komponentów ozdobionych formularzem redux, jeśli nie przetestowałeś samego pola „forma”.

Bądź konsekwentny w swoich wyborach, nie bierz pierwszego komponentu, który przychodzi ci na myśl, i zmieniaj logikę. Oczywiście struktura twojego projektu może się różnić. Może mieć inne nazwy katalogów lub dodatkowe komponenty, akcje i redukcje, ale logika definiowania kolejności testowania komponentów jest taka sama.

Zdefiniujmy, co należy pominąć w zakresie testu:

  • Biblioteki stron trzecich. Nie testuj funkcji pobranych z innej biblioteki. Nie jesteś odpowiedzialny za ten kod. Pomiń lub naśladuj implementację, jeśli potrzebujesz go do przetestowania kodu.
  • Stałe Nazwa mówi sama za siebie. Nie można ich zmieniać. Są to zestawy kodu statycznego, które nie mają się różnić.
  • Style wbudowane (jeśli używasz ich w komponencie). Aby przetestować style wbudowane, musisz powielić obiekt ze stylami w teście. Jeśli zmieniają się style obiektów, musisz je również zmienić w teście. Nie powielaj kodu komponentu w testach. Nigdy nie będziesz pamiętać o zmianie w testach. Co więcej, twój kolega nigdy nie zda sobie sprawy z tego, że doszło do duplikacji. W większości przypadków style wbudowane nie zmieniają zachowania komponentu, dlatego nie należy ich testować. Może istnieć wyjątek, jeśli twoje style zmieniają się dynamicznie.
  • Rzeczy niezwiązane z testowanym komponentem. Pomiń pokrycie testowymi komponentami zaimportowanymi do testowanego komponentu. Uważaj, jeśli jest zapakowany w inny. Nie testuj opakowania, po prostu przeanalizuj je i przetestuj osobno.

Jak zatem piszesz testy? Łączę dwa podejścia testowe:

  • Testowanie migawek
  • Testowanie logiki komponentów

Omówię je teraz oba.

Jak testować za pomocą migawek

Testowanie migawek to przydatne narzędzie do testowania na wypadek, gdybyś chciał mieć pewność, że interfejs użytkownika się nie zmienił. W przypadku tego narzędzia do testowania po raz pierwszy możesz mieć pytania dotyczące organizacji i zarządzania migawkami. Zasada jest bardzo prosta, ale niestety nigdzie jej nie opisano.

Krok 1. Napisz test dla komponentu, aw bloku oczekiwania użyj metody .toMatchSnapshot (), która tworzy samą migawkę:

it („renderuj poprawnie komponent tekstowy”, () => {
    const TextInputComponent = renderer.create (). toJSON ();
    expect (TextInputComponent) .toMatchSnapshot ();
});

Krok 2. Kiedy uruchomisz test po raz pierwszy na jednym poziomie, wraz z testem zostanie utworzony katalog o nazwie __snapshots__ z automatycznie wygenerowanym plikiem w środku z rozszerzeniem.snap.

Migawka wygląda następująco:

// Jest Snapshot v1, https://goo.gl/fbAQLP
eksportuje [`Renderuj TextInput poprawnie komponent 1`] =`

`;

Krok 3. Wciśnij migawkę do repozytorium i zapisz ją wraz z testem.

Jeśli składnik został zmieniony, wystarczy zaktualizować migawkę flagą —updateSnapshot lub użyć krótkiej flagi u.

Tak więc migawka jest tworzona - jak to działa?

Rozważmy dwa przypadki:

1. Składnik się zmienił

  • Przeprowadź testy
  • Utworzono nową migawkę, która porównuje z automatycznie wygenerowaną migawką zapisaną w katalogu __snapshots__
  • Testy nie powiodły się, ponieważ migawka jest inna

2. Składnik się nie zmienił

  • Przeprowadź testy
  • Utworzono nową migawkę, która porównuje z automatycznie wygenerowaną migawką zapisaną w katalogu __snapshots__
  • Testy przeszły pomyślnie, ponieważ migawka jest identyczna

Wszystko jest w porządku, gdy testujemy mały komponent bez logiki (tylko renderowanie interfejsu użytkownika). Ale jak pokazuje praktyka, nie ma takich elementów w prawdziwych projektach. Jeśli istnieją, jest ich niewiele.

Czy jest wystarczająco dużo migawek do pełnego testowania komponentów?

Główne instrukcje do testowania komponentów

1. Jeden składnik powinien mieć tylko jedną migawkę.

Jeśli jedna migawka się nie powiedzie, najprawdopodobniej inne też się nie powiedzie. Nie twórz i nie przechowuj wielu niepotrzebnych migawek zapychających przestrzeń i mylących programistów, którzy będą czytać twoje testy po tobie.

Oczywiście są wyjątki, gdy trzeba przetestować zachowanie komponentu w dwóch stanach: na przykład w stanie komponentu przed otwarciem okna podręcznego i po otwarciu.

Jednak nawet taki wariant można zawsze zastąpić tym: pierwszy test przechowuje domyślny stan komponentu bez wyskakującego okienka w migawce, a drugi test symuluje zdarzenie i sprawdza obecność określonej klasy. W ten sposób możesz łatwo ominąć tworzenie kilku migawek.

2. Testowanie rekwizytów

Z reguły dzielę testowanie rekwizytów na dwa testy:

  • Najpierw sprawdź renderowanie domyślnych wartości prop. Kiedy komponent jest renderowany, oczekuję, że wartość będzie równa defaultProps w przypadku, gdy ten prop ma defaultProps.
  • Po drugie, sprawdź niestandardową wartość rekwizytu. Ustawiam własną wartość i oczekuję, że zostanie ona otrzymana po renderowaniu komponentu.

3. Testowanie typów danych

W celu przetestowania, jaki typ danych występuje w rekwizytach lub jaki rodzaj danych jest uzyskiwany po określonych działaniach, możemy użyć specjalnej biblioteki jest-Extended (dodatkowe elementy dopasowujące Jest), która ma rozszerzony zestaw dopasowań, których nie ma w Żart. Dzięki tej bibliotece testowanie typów danych jest znacznie łatwiejsze i przyjemniejsze.

Z drugiej strony testowanie proptypów jest sprzecznym pytaniem. Niektórzy programiści mogą argumentować przeciwko testowaniu typów, ponieważ jest to pakiet innej firmy i nie należy go testować. Nadal nalegam na testowanie proptypów komponentów, ponieważ nie testuję samej funkcjonalności pakietu. Zamiast tego zapewniam tylko, że typy są poprawne. Typ danych jest bardzo ważną częścią programowania i nie należy go pomijać.

4. Testowanie zdarzeń

Po utworzeniu migawki i pokryciu rekwizytów testami możesz być pewien, że komponent będzie renderowany poprawnie. Ale to nie wystarcza do pełnego pokrycia w przypadku, gdy masz zdarzenia w komponencie.

Możesz sprawdzić zdarzenie na kilka sposobów. Najczęściej stosowane są:

  • próbne zdarzenie => symuluj to => oczekuj wywołania zdarzenia
  • próbne zdarzenie => symuluj zdarzenie z parametrami => oczekuj, że zdarzenie zostało wywołane z przekazanymi parametrami
  • przekaż niezbędne rekwizyty => renderuj komponent => symuluj zdarzenie => oczekuj określonego zachowania na wywołanym zdarzeniu

5. Warunki testowania

Bardzo często możesz mieć warunki dla wyjścia konkretnej klasy, renderowania określonej sekcji kodu, przesyłania wymaganych rekwizytów i tak dalej. Nie zapominaj o tym, ponieważ przy wartościach domyślnych tylko jedna gałąź przejdzie test, a druga pozostanie nietestowana.

W złożonych komponentach z obliczeniami i wieloma warunkami możesz przeoczyć niektóre gałęzie. Aby upewnić się, że wszystkie części kodu są objęte testami, użyj narzędzia pokrycia testowego i wizualnie sprawdź, które gałęzie są objęte, a które nie.

6. Stan testowania

Aby sprawdzić stan, w większości przypadków konieczne jest napisanie dwóch testów:

  • Pierwszy sprawdza bieżący stan.
  • Drugi sprawdza stan po wywołaniu zdarzenia. Renderuj komponent => wywołaj funkcję bezpośrednio w teście => sprawdź, jak zmienił się stan. Aby wywołać funkcję komponentu, musisz uzyskać jego instancję, a dopiero potem wywołać jej metody (przykład pokazano w następnym teście).

Po przejściu przez tę listę instrukcji, twój komponent zostanie objęty od 90 do 100%. Zostawiam 10% na specjalne przypadki, które nie zostały opisane w artykule, ale mogą wystąpić w kodzie.

Przykłady testowania

Przejdźmy do przykładów i omówmy komponenty testami, jak opisano powyżej krok po kroku.

1. Testowanie komponentu na podstawie formularzy / danych wejściowych.

Weź jeden komponent z katalogu form / input. Niech będzie to DateInput.js, składnik pola datepicker.

Lista kodów dla testowanego komponentu: DateInput.js
Wygląda jak:

Składnik DateInput korzysta z biblioteki Reaktywuj datepicker biblioteki, z dwoma narzędziami:

  • valueToDate (konwertuje wartość na datę)
  • dateToValue (konwertuje datę na wartość)

Pakiet służy do manipulowania datą, a PropTypes służy do sprawdzania rekwizytów React.

Zgodnie z kodem komponentu możemy zobaczyć listę domyślnych rekwizytów, które pomagają renderować komponent:

const defaultProps = {
    inputClassName: „input-custom”,
    miesiące Pokazano: 1,
    dateFormat: „DD.MM.RRRR”,
    showMonthYearsDropdowns: false,
    minDate: moment ()
};

Wszystkie rekwizyty są odpowiednie do tworzenia migawki, z wyjątkiem jednego: minDate: moment (). moment () podaje nam bieżącą datę za każdym razem, gdy uruchamiamy test, a migawka nie powiedzie się, ponieważ przechowuje nieaktualną datę. Rozwiązaniem jest kpić z tej wartości:

const defaultProps = {
    minDate: moment (0)
}

Potrzebujemy prop MinDate w każdym renderowanym komponencie. Aby uniknąć powielania rekwizytów, tworzę HOC, który odbiera defaultProps i zwraca ładny komponent:

importuj TestDateInput z „../DateInput”;
const DateInput = (rekwizyty) =>
    ;

Nie zapomnij o strefie czasowej, szczególnie jeśli Twoje testy będą przeprowadzane przez programistów z innego kraju w innej strefie czasowej. Otrzymają wyśmiewaną wartość, ale ze zmianą strefy czasowej. Rozwiązaniem jest ustawienie domyślnej strefy czasowej:

const moment = wymaga.requireActual („moment-strefa czasowa”). tz.setDefault („America / Los_Angeles”)

Teraz komponent wprowadzania daty jest gotowy do testowania:

1. Utwórz najpierw migawkę:

it („renderuj poprawnie komponent daty”, () => {
    const DateInputComponent = renderer.create (). toJSON ();
    expect (DateInputComponent) .toMatchSnapshot ();
});

2. Testowanie rekwizytów:

Przejrzyj rekwizyty i znajdź te ważne. Pierwszym testowanym rekwizytem jest showMonthYearsDropdowns. Jeśli ma wartość true, wyświetlane jest menu rozwijane dla miesiąca i lat:

it („wyświetla menu rozwijane miesiąca i roku”, () => {
    const props = {
            showMonthYearsDropdowns: true
        },
        DateInputComponent = mount ().find('.datepicker ');
    expect (DateInputComponent.hasClass ('reaguj-datepicker-ukryj-miesiąc')). toEqual (true);
});

Przetestuj zerową wartość prop. Ta kontrola jest wymagana, aby upewnić się, że komponent jest renderowany bez określonej wartości:

it („poprawnie renderuj datę z zerową wartością”, () => {
    const props = {
            wartość: null
        },
        DateInputComponent = mount ();
    expect ((DateInputComponent) .prop ('value')). toEqual (null);
});

3.Typy testowe wartości, oczekiwany ciąg znaków:

it („sprawdź typ wartości”, () => {
    const props = {
            wartość: „10 .03.2018”
        },
        DateInputComponent = mount ();
    expect (DateInputComponent.prop ('value')). toBeString ();
});

4. Wydarzenia testowe:

Najpierw sprawdź zdarzenie onChange.

  • mock onChange callback
  • wyrenderuj składnik wejściowy daty
  • symulacja zdarzenia zmiany z nową wartością docelową
  • i na koniec sprawdź, czy zdarzenie onChange zostało wywołane z nową wartością.
it („sprawdź wywołanie zwrotne onChange”, () => {
    const onChange = jest.fn (),
        rekwizyty = {
            wartość: „20 .01.2018”,
            onChange
        },
        DateInputComponent = mount ().find('input ');
    DateInputComponent.simulate („zmiana”, {cel: {wartość: moment ('2018-01-22')}});
    expect (onChange) .toHaveBeenCalledWith ('22 .01.2018 ');
});

Następnie upewnij się, że wyskakujące okienko datepicker otwiera się po kliknięciu wprowadzonej daty. W tym celu znajdź dane wejściowe => symuluj zdarzenie kliknięcia => i oczekuj wyskakującego okienka, gdy pojawi się klasa .react-datepicker.

it ('sprawdź wyskakujące okienko DatePicker otwarte', () => {
    const DateComponent = mount (),
        dateInput = DateComponent.find („input [type = 'text']”);
    dateInput.simulate („kliknięcie”);
    expect (DateComponent.find ('. reag-datepicker')). toHaveLength (1);
});

Pełny wykaz testów: DateInput.test.js

2. Testowanie użyteczności:

Lista kodów dla testowanego narzędzia: valueToDate.js

Celem tego narzędzia jest przekształcenie wartości na datę w niestandardowym formacie.

Przede wszystkim przeanalizujmy dane narzędzie i zdefiniuj główne przypadki testowania:

  1. Zgodnie z celem tego narzędzia przekształca wartość, dlatego musimy sprawdzić tę wartość:
  • W przypadku, gdy wartość nie jest zdefiniowana: musimy upewnić się, że narzędzie nie zwróci wyjątku (błąd).
  • W przypadku zdefiniowania wartości: musimy sprawdzić, czy narzędzie zwraca datę chwili.

2. Zwrócona wartość powinna należeć do klasy momentu. Dlatego powinien to być moment.

3. Drugi argument to dateFormat. Ustaw go jako stały przed testami. Dlatego będzie przekazywany w każdym teście i zwraca wartość zgodnie z formatem daty. Czy powinniśmy testować dateFormat osobno? Chyba nie. Ten argument jest opcjonalny - jeśli nie ustawimy dateFormat, narzędzie się nie zepsuje, a tylko zwróci datę w domyślnym formacie. To chwila pracy, nie powinniśmy testować bibliotek stron trzecich. Jak wspomniałem wcześniej, nie powinniśmy zapominać o strefie czasowej; jest to bardzo ważny punkt, szczególnie dla programistów z różnych stref czasowych.

Kodujmy:

  1. Napisz test dla pierwszego przypadku. Gdy nie mamy wartości, jest pusta.
const format = 'DD.MM.RRRR';
it („Narzędzie renderowania valueToDate z pustą wartością”, () => {
    const value = valueToDate ('', format);
    expect (wartość) .toEqual (null);
});

2. Sprawdź, czy wartość jest zdefiniowana.

const date = „21 .11.2015”,
      format = „DD.MM.RRRR”;
it („Narzędzie renderowania valueToDate o zdefiniowanej wartości”, () => {
    const value = valueToDate (data, format);
    expect (wartość) .toEqual (moment (data, format));
});

3. Sprawdź, czy wartość należy do klasy momentu.

const date = „21 .11.2015”,
    format = „DD.MM.RRRR”;
it („wartość kontrolna to instanceof moment”, () => {
    const value = valueToDate (data, format);
    expect (wartość instanceof moment) .toBeTruthy ();
});

Pełny wykaz testów: valueToDate.test.js

3. Testowanie widżetów

Do testowania widgetów wybrałem komponent typu spinner.

Lista kodów dla testowanego widgetu: Spinner.js

Wygląda tak:

Objaśnienie nie jest wymagane w objaśnieniu, ponieważ prawie wszystkie zasoby internetowe mają ten składnik.

Więc jeśli pójdziemy pisać testy:

  1. Pierwszy krok - utwórz migawkę:
it („renderuj poprawnie komponent Spinner”, () => {
   const SpinnerComponent = mount ();
   expect (SpinnerComponent) .toMatchSnapshot ();
});

2. Testowanie rekwizytów:

Najpierw sprawdzamy domyślny tytuł wniosku i sprawdzamy, czy jest on poprawnie renderowany.

it (domyślnie sprawdź tytuł wniosku), () => {
 const SpinnerComponent = mount ();
    expect (SpinnerComponent.find ('p'). text ()). toEqual ('Please wait');
});

Następnie sprawdzamy niestandardowy tytuł prop. Musimy sprawdzić, czy zwraca poprawnie zdefiniowany rekwizyt. Spójrz na kod, tytuł jest zawinięty w rawMarkup util, a dane wyjściowe za pomocą niebezpiecznieSetInnerHTML właściwości.

Lista kodów dla rawMarkup util:

eksportuj domyślną funkcję rawMarkup (szablon) {
    return {__html: template};
}

Czy musimy uwzględnić testy rawMarkup w komponencie przędzarki? Nie, jest to oddzielne narzędzie i powinno być testowane oprócz przędzarki. Nie obchodzi nas, jak to działa - musimy tylko wiedzieć, że tytuł prop zwraca poprawny wynik.

Wyjaśnienie: Powód użycia niebezpiecznieSetInnerHTML jest następujący. Nasza strona jest wielojęzyczna, za którą odpowiedzialny jest zespół marketingu tłumaczeń. Mogą przetłumaczyć to po prostu kombinacją słów lub nawet ozdobić tagami HTML, takimi jak , , , a nawet pokroić tekst za pomocą list

    ,
      . Nie wiemy na pewno, jak tłumaczą i dekorują tekst. Musimy tylko poprawnie wyrenderować wszystkie te rzeczy.

      W jednym teście połączyłem dwa główne przypadki testowe:

      • zwraca poprawny niestandardowy tytuł prop
      • poprawnie renderuj tytuł prop za pomocą tagów HTML
      it („sprawdź tytuł wniosku za pomocą tagów HTML”, () => {
          const props = {
                  tytuł: „ Proszę czekać ”
              },
              SpinnerComponent = mount ();
          expect (SpinnerComponent.find ('p'). text ()). toEqual ('Please wait');
      });

      Weź następny podtytuł prop. Jest opcjonalny i dlatego nie ma domyślnego rekwizytu, więc pomiń krok z domyślnymi rekwizytami i przetestuj rekwizyty niestandardowe:

      • Sprawdź, czy tekst w podtytule jest poprawnie renderowany:
      const props = {
              podtytuł: „pozostała 1 minuta”
          },
          SpinnerComponent = mount ();
      it („renderuj poprawny tekst”, () => {
          expect (SpinnerComponent.find ('p'). at (1) .text ()). toEqual (props.subTitle);
      });

      Wiemy, że podtytuł jest opcjonalny. Dlatego musimy sprawdzić, czy nie jest renderowany z domyślnymi rekwizytami, zgodnie ze znacznikiem krojenia. Po prostu sprawdź liczbę tagów

      :

      it ('check subTitle nie jest renderowany', () => {
        const SpinnerComponent = mount ();
          expect (SpinnerComponent.find ('p'). length) .toEqual (1);
      });

      3. Testowanie typów rekwizytów:

      • W przypadku tytułu prop ma być ciąg znaków:
      it („sprawdź typ prop dla tytułu to string”, () => {
          const props = {
                  tytuł: „Czekaj”
              },
              SpinnerComponent = mount ();
          expect (SpinnerComponent.find ('p'). text ()). toBeString ();
      });
      • W przypadku podtytułu należy się również spodziewać ciągu:
      const props = {
              podtytuł: „pozostała 1 minuta”
          },
          SpinnerComponent = mount ();
      it („type for subTitle is string”, () => {
          expect (SpinnerComponent.find ('p'). at (1) .text ()). toBeString ();
      });

      Pełny wykaz testów: Spinner.test.js

      4. Testowanie modów (ModalWrapper.js i ModalTrigger.js)

      Wygląda jak:

      Jak testować moduły

      Przede wszystkim chcę wyjaśnić, w jaki sposób moduły są zorganizowane w naszym projekcie. Mamy dwa składniki: ModalWrapper.js i ModalTrigger.js.

      ModalWrapper odpowiada za układ wyskakujących okienek. Zawiera pojemnik modalny, przycisk „zamknij”, tytuł i ciało modalne.

      ModalTrigger odpowiada za obsługę modalną. Obejmuje układ ModalWrapper i zawiera zdarzenia do kontroli układu modala (akcje otwierania i zamykania).

      Omówię każdy element osobno:

      1. Lista kodów dla testowanego komponentu: ModalWrapper.js

      Kodujmy:

      Najpierw ModalWrapper odbiera składnik i renderuje go w środku. Przede wszystkim sprawdź, czy ModalWrapper nie zawiedzie bez komponentu. Utwórz migawkę z domyślnymi rekwizytami:

      it („bez komponentu”, () => {
          const ModalWrapperComponent = płytkie ();
          expect (ModalWrapperComponent) .toMatchSnapshot ();
      });

      Następnym krokiem jest symulacja jego rzeczywistego stanu z renderowaniem komponentów przekazywanym przez rekwizyty:

      it ('with component', () => {
         const props = {
                 komponent: () => {}
              },
              ModalWrapperComponent = płytkie ();
          expect (ModalWrapperComponent) .toMatchSnapshot ();
      });

      Testowanie rekwizytów

      Odbieranie prop niestandardowej nazwy klasy:

      it („render poprawna nazwa klasy”, () => {
          const props = {
                  modalClassName: „nazwa klasy niestandardowej”
              },
              ModalWrapperComponent = płytkie ().find('Modal ');
              expect (ModalWrapperComponent.hasClass ('custom-class-name')). toEqual (true);
      });

      Odbieranie niestandardowego tytułu prop:

      it („render poprawnie tytuł”, () => {
          const props = {
                 tytuł: „tytuł modalny”
             },
             ModalWrapperComponent = płytki ().find('ModalTitle ');
          expect (ModalWrapperComponent.props (). children) .toEqual („Modal Title”);
      });

      Odbieranie poprawnej propozycji programu:

      it („sprawdź wartość prop”, () => {
              const props = {
                     pokaż: prawda
                 },
                 ModalWrapperComponent = płytkie ().find('Modal ');
              expect (ModalWrapperComponent.props (). show) .toEqual (true);
          });

      Testowanie proptypów

      • Na prop
      it („sprawdź typ prop”, () => {
          const props = {
                 pokaż: prawda
              },
              ModalWrapperComponent = płytkie ().find('Modal ');
          expect (ModalWrapperComponent.props (). show) .toBeBoolean ();
      });
      • Do prop. OnHide
      it („renderuj poprawny typ prop onHide”, () => {
          const props = {
                  onHide: () => {}
              },
              ModalWrapperComponent = płytkie ().find('Modal ');
          expect (ModalWrapperComponent.props (). onHide) .toBeFunction ();
      });
      • Dla podpory składowej
      it („renderuj poprawny typ prop komponentu”, () => {
         const props = {
                 komponent: () => {}
             },
             ModalWrapperComponent = mount ();
         expect (ModalWrapperComponent.props (). component) .toBeFunction ();
      });

      Pełny wykaz testów: ModalWrapper.test.js

      2. Lista kodów dla testowanego komponentu: ModalTrigger.js

      Owijka modalna została objęta testem. Druga część dotyczy pokrycia modalnego elementu wyzwalającego.

      Przegląd komponentów: jest oparty na przełączonym stanie wskazującym widoczność ModalWrapper. W przypadku przełączenia: false wyskakujące okno jest ukryte, w przeciwnym razie jest widoczne. Funkcja open () otwiera wyskakujące okienko na elemencie potomnym. Zdarzenie click i funkcja close () ukrywa wyskakujące okienko przycisku renderowanego w ModalWrapper.

      Tworzenie migawki:

      it („poprawnie renderuj moduł ModalTrigger”, () => {
          const ModalTriggerComponent = płytkie ( 
      );     expect (ModalTriggerComponent) .toMatchSnapshot (); });

      Czy powinniśmy przetestować ModalTrigger z renderowaniem prop komponentów? Nie - ponieważ komponent będzie renderowany w składniku ModalWrapper. Nie zależy to od testowanego komponentu. Został już objęty testami w testach ModalWrapper.

      Testowanie rekwizytów:

      Mamy jedno dziecko-rekwizytora i chcemy mieć pewność, że mamy tylko jedno dziecko.

      it („upewnij się, że masz tylko jedno dziecko (element sterujący)”, () => {
          expect (ModalTriggerComponent.findWhere (node ​​=> node.key () === 'modal-control'). length) .toEqual (1);
      });

      Testy typów:

      Podpora potomna powinna być obiektem, więc sprawdź to w następnym teście:

      const ModalTriggerComponent = mount ( 
      );
      it („sprawdź typ prop dla dzieci”, () => {
            expect (ModalTriggerComponent.props (). children) .toBeObject ();
      });

      Ważną częścią modułu ModalTrigger jest sprawdzanie stanów.

      Mamy dwa stany:

      • Wyskakujące okienko jest otwarte. Aby wiedzieć, że modal jest otwarty, musimy sprawdzić jego stan. W tym celu wywołaj funkcję open z instancji komponentu i spodziewaj się, że stan przełączony powinien być prawdziwy.
      it („sprawdź, czy modal jest otwarty”, () => {
          const event = {
              preventDefault: () => {},
              stopPropagation: () => {}
          };
          ModalTriggerComponent.instance (). Open (event);
          expect (ModalTriggerComponent.state (). toggled) .toBeTruthy ();
      });
      • Wyskakujące okienko jest zamknięte. Jest testowany odwrotnie, przełączony w stanie powinien być fałszywy.
      it („sprawdź, czy modal jest zamknięty”, () => {
         ModalTriggerComponent.instance (). Close ();
         expect (ModalTriggerComponent.state (). toggled) .toBeFalsy ();
      });

      Pełny wykaz testów: ModalTrigger.test.js

      Teraz moduły są w pełni testowane. Jedna rada na temat testowania komponentów, które są od siebie zależne: najpierw przejrzyj komponenty i napisz plan testów, zdefiniuj, co chcesz przetestować w każdym komponencie, sprawdź przypadki testowe dla każdego komponentu i upewnij się, że nie powtórz ten sam przypadek testowy dla obu komponentów. Dokładnie przeanalizuj możliwe i optymalne warianty pokrycia testowego.

      5. Testy HOC (komponent wyższego rzędu)

      Dwie ostatnie części (testy HOC i pola formularza) są ze sobą powiązane. Chciałbym się z Wami podzielić, jak przetestować układ pola za pomocą HOC.

      Oto wyjaśnienie, czym jest BaseFieldLayout, dlaczego potrzebujemy tego składnika i gdzie go używamy:

      • BaseFieldLayout.js to opakowanie dla komponentów wejściowych formularza, takich jak TextInput, CheckboxInput, DateInput, SelectInput itp. Ich nazwy kończą się na -Input, ponieważ używamy pakietu redux-form, a te komponenty są wejściowymi komponentami logiki redux-form.
      • Potrzebujemy BaseFieldLayout do tworzenia układu dla komponentów pól formularza, czyli renderowania etykiety, podpowiedzi, prefiksów (waluty, skróty w metrach kwadratowych itp.), Ikon, błędów i tak dalej.
      • Używamy go w BaseFieldHOC.js do zawijania inputComponent w układzie pola i łączenia go z formularzem redux za pomocą komponentu .

      Lista kodów dla testowanego komponentu: BaseFieldHOC.js

      Jest to HOC, który odbiera komponent wejściowy formularza i zwraca komponent połączony z formą redux.

      Analiza HOC:

      • Ten komponent otrzymuje tylko jeden rekwizyt, komponent. Przede wszystkim muszę utworzyć ten komponent i zawinąć go w BaseFieldHOC.
      • Następnie muszę ozdobić zawinięty HOC formularzem redux, aby połączyć pole z formularzem redux.
      • Renderuj to pole w komponencie React Redux , aby udostępnić sklep testowanemu komponentowi. Aby wyśmiewać sklep, po prostu wykonaj:
      const store = createStore (() => ({}));

      Teraz przed każdym testem muszę wykonać następujące czynności:

      niech BaseFieldHOCComponent;
      beforeEach (() => {
          const TextInput = () => {return 'input text'; },
              BaseFieldHOCWrapper = BaseFieldHOC (TextInput),
              TextField = reduxForm ({form: 'testForm'}) (BaseFieldHOCWrapper);
          BaseFieldHOCComponent = renderer.create (
              
                  
              
          ) .toJSON ();
      });

      Następnie komponent jest gotowy do testowania:

      1. Utwórz migawkę:
      it („renderuj poprawnie komponent”, () => {
          expect (BaseFieldHOCComponent) .toMatchSnapshot ();
      });

      2. Upewnij się, że składnik wejściowy jest zawinięty w BaseFieldLayout po renderowaniu:

      it („sprawdź, czy składnik wejściowy jest zawinięty w BaseFieldLayout”, () => {
          expect (BaseFieldHOCComponent.props.className) .toEqual ('form-group');
      });

      To wszystko, HOC jest objęty. Najbardziej skomplikowaną częścią testowania komponentów związanych z formularzem redux jest przygotowanie pola (udekorowanie formularzem redux i konfiguracja sklepu). Reszta jest łatwa, wystarczy postępować zgodnie z instrukcjami i nic więcej.

      Pełny wykaz testów: BaseFieldHOC.test.js

      6. Testowanie formularzy / pól

      Pole HOC jest objęte testami, dzięki czemu możemy przejść do składnika BaseFieldLayout.

      Lista kodów dla testowanego komponentu: BaseFieldLayout.js

      Napiszmy kod BaseFieldLayout.js i napisz testy zgodnie z powyższymi instrukcjami:

      1. Przede wszystkim utwórz migawkę.

      Ten komponent nie będzie renderowany bez defaultProps:

      • inputComponent
      • Rekwizyty dostarczone przez redux-form: input i meta objects. Wprowadź nazwę właściwości i meta z błędem właściwości i dotknij:
      const defaultProps = {
         meta: {
              dotknięty: zero,
              błąd: null
          },
          Wejście: {
              nazwa: „nazwa pola”
          },
          inputComponent: () => {return 'test case'; }
      }

      Aby użyć defaultProps w każdym testowanym opakowaniu, wykonaj następujące czynności:

      importuj TestBaseFieldLayout z „../BaseFieldLayout”;
      const BaseFieldLayout = (rekwizyty) => ;

      Teraz jesteśmy gotowi do utworzenia migawki:

      it („renderuj poprawnie komponent BaseFieldLayout”, () => {
          const BaseFieldLayoutComponent = renderer.create (). toJSON ();
          expect (BaseFieldLayoutComponent) .toMatchSnapshot ();
      });

      2. Testowanie rekwizytów:

      Ten komponent ma wiele rekwizytów. Pokażę kilka przykładów, a reszta zostanie przetestowana przez analogię.

      • Upewnij się, że ikona prop jest renderowana poprawnie
      it („renderuj poprawnie ikonę prop”, () => {
          const props = {
                  icon: 
              },
              BaseFieldLayoutComponent = mount ();
              expect (BaseFieldLayoutComponent.find („span”). hasClass („wykrzyknik”)). toBeTruthy ();
      });
      • Upewnij się, że treść podpowiedzi wyświetla się obok etykiety
      const props = {
              labelTooltipContent: „etykietka dla etykiety”
          },
          BaseFieldLayoutComponent = mount ();
      it („sprawdź prop jest renderowany”, () => {
         expect (BaseFieldLayoutComponent.find ('span'). hasClass ('tooltip-icon')). ​​toBeTruthy ();
      });
      • Pole testowe Link prop
      • Upewnij się, że fieldLink ma domyślnie wartość NULL
      it („domyślnie sprawdź prop jest zerowy”, () => {
          const BaseFieldLayoutComponent = płytkie ();
          expect (BaseFieldLayoutComponent.props (). fieldLink) .toBe (null);
      });
      • Upewnij się, że fieldLink renderuje poprawnie z wartością niestandardową

      3. Testowanie błędów:

      it („sprawdź, czy pole zawiera błąd”, () => {
          const props = {
                  meta: {
                      dotknięty: prawda,
                      błąd: „To pole jest wymagane”
                  }
              },
              BaseFieldLayoutComponent = mount ();
          expect (BaseFieldLayoutComponent.find ('. error')). toHaveLength (1);
      });

      Pełny wykaz testów: BaseFieldLayout.test.js

      Dolna linia

      Teraz wiesz, jak przeprowadzić pełne testowanie pokrycia komponentów w oparciu o strukturę projektu. Z własnego doświadczenia starałem się wyjaśnić, co jest konieczne do przetestowania, w jakiej kolejności i co można pominąć w zakresie testu. Pokazałem również przykłady kilku składników testowych i zauważyłem sekwencję pokrycia bazy kodu.

      Mam nadzieję, że ten artykuł okaże się przydatny i podzielisz się swoimi odpowiedziami. Dziękuję za przeczytanie.

      Jeśli uznasz ten post za przydatny, kliknij przycisk poniżej :)