Zanurzmy się w cyprys do kompleksowego testowania

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Czy kompleksowe testowanie jest dla Ciebie bolesnym tematem? W tym artykule Ramona Schwering wyjaśnia, jak radzić sobie z kompleksowymi testami Cypress i sprawić, by nie było to dla ciebie tak żmudne i drogie, ale zamiast tego zabawne.

Tworzenie oprogramowania bez testów automatycznych jest dziś trudne do wyobrażenia. Duża różnorodność różnych procedur testowych zapewni wysoki poziom jakości. Jako podstawę do testowania możemy wykorzystać szereg testów jednostkowych. Na dodatek pośrodku piramidy, że tak powiem, znajdują się testy integracyjne. Testy end-to-end znajdują się na samym szczycie, obejmując najbardziej krytyczne przypadki użycia. Ten trzeci rodzaj testowania będzie tematem tego artykułu.

Jednak testowanie end-to-end ma pewne pułapki , które są powodem do niepokoju:

  • Testy typu end-to-end są powolne, a zatem stanowią istotną przeszkodę w każdej strategii ciągłej integracji i ciągłego wdrażania (CI/CD). Nie tylko to, ale wyobraź sobie zakończenie zadania, funkcji lub jakiejkolwiek innej implementacji — oczekiwanie na wykonanie testu może wyczerpać cierpliwość każdego.
  • Takie kompleksowe testy są trudne do utrzymania, podatne na błędy i kosztowne pod każdym względem ze względu na wysiłek związany z debugowaniem. Mogą to powodować różne czynniki. Twój test powinien sprawiać wrażenie asystenta, a nie przeszkody.
  • Największym koszmarem dla programistów jest niestabilny test, który jest wykonywany w ten sam sposób, ale prowadzi do różnych wyników. To jest jak „Heisenbug”, który pojawia się tylko wtedy, gdy nie mierzysz testowanej aplikacji — to znaczy, jeśli na nią nie patrzysz.
Heisenbugs
Heisenfails są prawdziwe i podobne do Heisenbugs. (duży podgląd)

Ale nie martw się: nie musisz wpadać w te pułapki. Zobaczmy, jak zapobiec wielu z nich . Jednak nie tylko obiecuję księżyc i nie dostarczę. W tym przewodniku napiszemy razem kilka testów, które upubliczniłem dla Ciebie w repozytorium GitHub. W ten sposób mam nadzieję pokazać, że końcowe testowanie może być świetną zabawą! Zacznijmy.

Czym są testy typu end-to-end?

Mówiąc o testowaniu end-to-end (lub E2E), lubię określać je jako „oparte na przepływie pracy”. To zdanie dobrze podsumowuje testy end-to-end: symuluje rzeczywiste przepływy pracy użytkownika i powinno obejmować jak najwięcej obszarów funkcjonalnych i części stosu technologicznego używanego w aplikacji. W końcu komputer udaje klienta i stara się zachowywać jak prawdziwy użytkownik. Testy te najlepiej nadają się do ciągłego obciążania całego systemu aplikacji, a zatem są doskonałym sposobem zapewnienia jakości , gdy obecny jest cały stos aplikacji.

Robot
Testy end-to-end są wykonywane przez komputer symulujący rzeczywistego użytkownika. (duży podgląd)

Przypomnijmy sobie, co chcemy przez to wszystko osiągnąć. Wiemy, że testowanie front-endu to zestaw praktyk testowania interfejsu użytkownika aplikacji internetowej, w tym jej funkcjonalności. Ma sens — dzięki tym środkom możemy zapewnić, że nasza aplikacja działa poprawnie i że żadne przyszłe zmiany nie spowodują złamania naszego kodu. Aby skutecznie to osiągnąć, możesz się zastanawiać, co i ile musisz przetestować.

To jest ważne pytanie. Możesz znaleźć jedną możliwą odpowiedź w metaforze: Piramida automatyzacji testów, po raz pierwszy wprowadzona przez Mike'a Cohna, a następnie doprecyzowana przez Martina Fowlera, pokazuje, jak sprawić, by testowanie było wydajne . Szybkie i tanie testy jednostkowe znajdują się na najniższym poziomie piramidy, a czasochłonne i drogie testy interfejsu użytkownika (testy end-to-end) na górze.

Piramida testowa
Piramida testów automatyzacji. (duży podgląd)

Wyjaśnienie tego oraz jego zalet i wad wystarczyłoby na własny artykuł. Chciałbym skupić się na jednym poziomie. Zwłaszcza testy typu end-to-end mogą przynieść znaczną poprawę jakości, jeśli zostaną skutecznie uszeregowane pod względem priorytetów. W ten sposób możemy stale obciążać nasz system i zapewniać prawidłowe działanie głównych funkcji naszej aplikacji.

Moja podróż do cyprysu

Kiedy zacząłem uczyć się pisania testów end-to-end, użyłem Mink, biblioteki PHP, a także Behat, frameworka BDD (behawioralnego zorientowanego na scenariusz). Zacząłem używać Selenium, ze wszystkimi jego zaletami i wadami. Ponieważ mój zespół zaczął dużo pracować z Vue.js, przeszliśmy na platformę testową opartą na JavaScript, aby zapewnić bezbłędną integrację i kompatybilność. Wtedy naszym wyborem był Nightwatch.js, więc zbudowałem nasz nowy zestaw testowy od podstaw.

W tym czasie często natykaliśmy się na problemy ze zgodnością . Można to nazwać piekłem zależności — nie wspominając o wszystkich ograniczeniach, które widzieliśmy w Selenium, a później w WebDriver.

  • W naszym zespole nie byliśmy w stanie określić wersji naszego CI dla Chrome. Jeśli więc pojawiły się aktualizacje Chrome, Nightwatch.js nie był wystarczająco szybki, aby był kompatybilny, powodując wiele błędów w naszych testach.
  • Liczba stron testowych przyczyn niestabilnych testów zaczęła rosnąć, ponieważ możliwości oczekiwania Nightwatch.js nie były optymalnie dopasowane do naszego produktu.

Zaczęliśmy więc rozważać zbudowanie naszego zestawu testowego od nowa. Po wizycie na niekonferencji odkryłem Cypress.

Cypress to kompleksowa platforma testowa, która nie korzysta z Selenium ani WebDriver. Narzędzie używa Node.js do uruchamiania przeglądarki pod specjalną kontrolą. Testy w tym frameworku są przeprowadzane na poziomie przeglądarki, a nie tylko przy zdalnym sterowaniu. Daje to kilka korzyści.

W skrócie, oto powody, dla których wybrałem ten framework:

  • Doskonałe możliwości debugowania
    Program uruchamiający testy Cypress może przeskoczyć z powrotem do dowolnego stanu aplikacji za pomocą migawek. Możemy więc bezpośrednio zobaczyć błąd i wszystkie poprzedzające go kroki. Ponadto istnieje pełny dostęp do narzędzi programistycznych Chrome (DevTools), a kliknięcia są w pełni rejestrowane.
  • Lepsze sposoby oczekiwania na akcje w teście lub interfejsie użytkownika lub w odpowiedziach z API
    Cypress niesie ze sobą ukryte oczekiwanie, więc nie ma potrzeby przeprowadzania odpowiednich kontroli. Możesz także sprawić, by test czekał na animacje i odpowiedzi API.
  • Testy są pisane w JavaScript
    To łagodzi krzywą uczenia się pisania testów. Program uruchamiający testy Cypress jest oprogramowaniem typu open source, więc pasuje do naszej strategii produktowej.

Jednak ten artykuł jest przewodnikiem, więc zatrzymajmy się na tych ogólnych informacjach i zacznijmy.

Pierwsze kroki

Zainstaluj i uruchom Cyprys

Zacznijmy od zera. W moich rozmowach na temat Cypressa zwykle zaczynam od stworzenia nowego katalogu przez mkdir , a następnie od razu instaluję Cypress. Najłatwiejszy sposób instalacji pokazano na tym rysunku:

Cypress używa Node.js
Cypress używa Node.js (duży podgląd)

Mała podpowiedź: Jeśli nie chcesz używać npm, możesz zainstalować Cypress przez Yarn:

 yarn add cypress --dev

Alternatywą jest bezpośrednie pobieranie za pomocą folderów ZIP, które zapewnia Cypress. Otóż ​​to! Po zakończeniu instalacji możesz rozpocząć.

Testy Cypress można rozpocząć na dwa sposoby. Pierwszym z nich jest uruchomienie Cypressa w konsoli i bezgłowe przeprowadzanie testów:

 ./node_modules/.bin/cypress run

Drugim sposobem jest użycie jednej z fajnych funkcji Cypress, którą jest zintegrowany program do testowania. Test runner to interfejs użytkownika do przeprowadzania testów. Aby go uruchomić, możesz użyć podobnego polecenia:

 ./node_modules/.bin/cypress open

To polecenie otworzy tester. Gdy otworzysz Cypress po raz pierwszy, zobaczysz ten interfejs:

Biegacz testowy Cyprysa
Biegacz testowy Cypress od pierwszego wejrzenia. (duży podgląd)

Cypress udostępnia kilka gotowych, przykładowych testów, aby zaprezentować swoje funkcje i dać ci kilka punktów wyjścia — to jest powód dostępnych testów. Pomińmy je na razie, bo niedługo chcemy napisać własne. Należy jednak pamiętać o tym obszarze „Testy integracyjne”, ponieważ uwzględni on wiele magii, która wydarzy się później.

Pierwsze wrażenie struktury cyprysu

Teraz nadszedł czas, aby otworzyć nasz nowo utworzony projekt w wybranym przez nas zintegrowanym środowisku programistycznym (IDE). Jeśli przejdziesz do tego folderu, zobaczysz następującą strukturę testu:

 smashing-example └── cypress └── fixtures └── integration └── plugins └── support └── cypress.json

Przyjrzyjmy się tym folderom:

  • fixtures
    Tutaj znajdziesz stałe dane testowe, które nie mają związku z innymi jednostkami. Dlatego nie są tu przechowywane żadne identyfikatory, które mogą się zmieniać w zależności od stanu lokalnego.
  • integration
    Aktualne testy znajdziesz tutaj.
  • plugins
    Tutaj możesz rozszerzyć Cypress, czy to za pomocą istniejących wtyczek Cypress, czy też własnych.
  • support
    Tutaj możesz rozszerzyć sam Cypress. Tutaj znajdują się Twoje własne polecenia i pomocnicy.
  • cypress.json
    Zmodyfikuj tutaj konfiguracje, w tym dla środowiska.

W porządku, myślę, że teraz możemy znaleźć sposób na Cypress, niezależnie od tego, czy jest to program uruchamiający testy, czy kod źródłowy. Ale jak zaczniemy? Co chcemy przetestować?

Wybierz przypadek testowy

Typowy test typu end-to-end może być złożony, zwłaszcza jeśli składa się z wielu kroków. Wykonanie ręczne zajęłoby dużo czasu. Ze względu na tę złożoność testy E2E mogą być trudne do zautomatyzowania i powolne. W rezultacie musimy dokładnie zdecydować, które przypadki zautomatyzować.

Moim zdaniem kluczowy jest termin „workflow-based” : wybralibyśmy przypadki testowe na podstawie typowych historyjek użytkownika. Jednak ze względu na czas wykonywania nie zaleca się uwzględniania każdego dostępnego przepływu pracy. Dlatego potrzebujemy sposobu na ustalenie priorytetów naszych przypadków testowych.

W moim zespole mieliśmy kilka kryteriów dla naszego projektu. Przypadek testowy powinien:

  • obejmują najbardziej ogólne i najczęściej używane przepływy pracy funkcji, takie jak operacje CRUD (termin „szczęśliwa ścieżka” dość dobrze opisuje te przepływy);
  • stosować analizę ryzyka obejmującą przepływy pracy z testami E2E, które są najbardziej podatne (tj. tam, gdzie błędy spowodowałyby największe szkody);
  • unikać powielania zasięgu;
  • niekoniecznie muszą być używane, jeśli bardziej odpowiednie są testy jednostkowe (użyj testu E2E, aby przetestować reakcję oprogramowania na błąd, a nie sam błąd).

Drugą najważniejszą rzeczą, o której należy pamiętać, jest testowanie tylko przepływu pracy, który wyraźnie chcesz przetestować. Wszystkie inne kroki wymagane do wykonania testu należy wykonać za pomocą operacji API poza testem, aby uniknąć ich testowania. W ten sposób zapewnisz minimalne czasy wykonywania testów i uzyskasz jasny wynik przypadku testowego, jeśli się nie powiedzie. Pomyśl o tym przepływie pracy tak, jak użytkownik końcowy: Skoncentruj się na korzystaniu z funkcji, a nie na implementacji technicznej .

Przykład:

Jeśli chcesz przetestować proces realizacji transakcji w sklepie internetowym, nie wykonuj wszystkich pozostałych kroków, takich jak tworzenie produktów i kategorii, nawet jeśli będziesz ich potrzebować do realizacji transakcji. Użyj na przykład API lub zrzutu bazy danych, aby zrobić te rzeczy i skonfiguruj test tylko dla kasy.

Przykład: Znajdowanie moich artykułów w Smashing Magazine

Chcę napisać test dla tej strony internetowej Smashing Magazine. Nie mogę zagwarantować, że ten test będzie zawsze aktualny, ale miejmy nadzieję, że będzie trwał. Tak czy inaczej, będziesz mógł znaleźć ten przykład w repozytorium GitHub.

Tworzenie naszego pierwszego testu na cyprys

W folderze integration zaczniemy od utworzenia nowego pliku. Nazwijmy to find-author.spec.js . Przyrostek .spec oznacza „specyfikację”. W ujęciu testowym odnosi się to do szczegółów technicznych danej funkcji lub aplikacji, które musi spełniać Twoja aplikacja.

Aby zmienić ten pusty plik JavaScript w stronę główną testu, zaczniemy od nadania zestawowi testów jego struktury. Użyjemy metody describe nazwie description . describe() lub context() służy do przechowywania i organizowania testów. Innymi słowy, ta metoda służy jako rama dla naszych testów. Zatem nasz plik testowy będzie wyglądał tak:

 // find-author.spec.js describe('Find authors at smashing', () => { //... });

Następnym krokiem jest stworzenie właściwego testu. Użyjemy metody it . it() lub specify() służy do reprezentowania rzeczywistego testu. Jak widać, możemy przechwycić wiele testów w jednym pliku, co daje kilka doskonałych opcji strukturyzacji.

 // find-author.spec.js describe('Find authors at smashing', () => { it('Find the author Ramona Schwering', () => { cy.log('This is our brand-new test'); }); });

Mała wskazówka : Jeśli znasz Mokkę, być może zauważyłeś pewne podobieństwa. Cypress jest zbudowany na Mocha, więc składnia jest taka sama.

W porządku, przejdźmy dalej. Jeśli uruchomimy nasz test w programie uruchamiającym testy Cypress, zauważymy, że Cypress otworzy przeglądarkę, aby przeprowadzić test. Ta przeglądarka jest widoczna na poniższym zrzucie ekranu:

Wykonanie naszego pierwszego testu
Nasz pierwszy test, wykonany w programie testowym Cypressa. (duży podgląd)

Gratulacje! Napisaliśmy nasz pierwszy test! Jasne, niewiele to robi. Musimy kontynuować. Wypełnijmy nasz test życiem.

Wypełnij test życiem

Jaka jest pierwsza rzecz do zrobienia podczas testowania strony internetowej? Racja, musimy otworzyć stronę internetową. Możemy to zrobić za pomocą polecenia Cypress. Jakie jest polecenie, możesz się zastanawiać?

Praca z poleceniami

W teście E2E stosuje się głównie dwa rodzaje instrukcji. Pierwszy typ instrukcji, komendy, reprezentuje poszczególne etapy testu. W kontekście Cypress polecenia to wszystko, co Cypress robi w celu interakcji z Twoją witryną. Ta interakcja może być wszystkim — kliknięciem, przewinięciem strony internetowej, a nawet znalezieniem elementu. W rezultacie polecenia będą jedną z ważnych rzeczy, którymi wypełnimy nasz test.

Tak więc naszym pierwszym poleceniem będzie przejście do witryny — smashingmagazine.com . To polecenie nazywa się visit .

Używając go, nasz test będzie wyglądał tak:

 // find-author.spec.js describe('Find authors at smashing', () => { it('Find the author Ramona Schwering', () => { cy.visit('https://www.smashingmagazine.com/'); }); });

Jest jedno polecenie, którego często używam — i ty też to zrobisz. Nazywa get :

 cy.get('selector');

To polecenie zwraca element zgodnie z jego selektorem — podobnie do $(…) w jQuery. Tak więc użyjesz tego polecenia, aby znaleźć części do interakcji. Zwykle używałbyś go do uruchomienia łańcucha poleceń. Ale czekaj — co oznacza łańcuch poleceń?

Jak wspomniano na początku tego artykułu, wszystkie testy i wszystko, co się z nimi wiąże, są napisane w JavaScript. Możesz umieścić polecenia w testach (tzn. instrukcje) w łańcuchu (innymi słowy, w łańcuchu). Oznacza to, że polecenia mogą przekazać temat (lub zwrócić wartość) polecenia do następnego polecenia, jak wiemy z wielu frameworków testowych.

W porządku, zaczniemy łańcuch poleceń od polecenia get . Aby znaleźć element za pomocą get , musimy najpierw znaleźć jego selektor. Znalezienie unikalnego selektora jest niezbędne, ponieważ w przeciwnym razie Cypress zwróciłby wszystkie pasujące elementy; więc miej to na uwadze i unikaj tego, jeśli jest to niezamierzone.

Interakcja z elementami

Sam Cypress ma funkcję, która pomoże Ci znaleźć selektory elementów, z którymi chcesz pracować. Ta funkcja nosi nazwę Plac zabaw selektorów i pomaga odkryć unikalne selektory komponentu lub zobaczyć wszystkie pasujące elementy dla selektora lub ciągu tekstowego. Tak więc ta funkcja może ci bardzo pomóc w tym zadaniu. Aby go włączyć, po prostu kliknij ikonę celownika w nagłówku interfejsu użytkownika testu, a następnie najedź kursorem na żądany element:

Korzystanie z placu zabaw selektora
Korzystanie z selektora Playground do identyfikowania unikalnych selektorów. (duży podgląd)

Jak widać na powyższym zrzucie ekranu, podpowiedź wyświetli selektor po najechaniu kursorem lub na tym małym pasku pod ikoną celownika, która pojawiła się po kliknięciu elementu. Na tym pasku możesz też zobaczyć, ile elementów pasowałoby do danego selektora — zapewniając jego unikalność w naszym przypadku.

Czasami te automatycznie generowane selektory mogą nie być tymi, których chcesz użyć (np. jeśli są długie lub trudne do odczytania lub nie spełniają innych kryteriów). Wygenerowany poniżej selektor jest trudny do zrozumienia i moim skromnym zdaniem zbyt długi:

Nie jest to idealny selektor
Ten selektor może nie być idealny w użyciu. (duży podgląd)

W takim przypadku skorzystałbym z DevTools przeglądarki, aby znaleźć moje unikalne selektory. Być może znasz te narzędzia; w moim przypadku często wybieram w tym celu Chrome. Jednak inne obsługiwane przeglądarki mogą oferować podobne funkcje. Proces wygląda podobnie jak w przypadku Selector Playground, z tą różnicą, że korzystamy z funkcji DevTools w zakładce „Element”.

Znajdowanie selektorów za pomocą narzędzi programistycznych
Korzystanie z narzędzi programistycznych przeglądarki do znajdowania unikalnych selektorów. (duży podgląd)

Aby upewnić się, że selektor jest unikalny, polecam wyszukanie go w widoku kodu DevTools. Jeśli znajdziesz tylko jeden wynik, możesz mieć pewność, że jest wyjątkowy.

Czy wiesz, że istnieje wiele różnych typów selektorów ? W zależności od odmiany testy mogą wyglądać, a nawet zachowywać się zupełnie inaczej. Niektóre odmiany są lepiej przystosowane do testów typu end-to-end niż inne. Jeśli chcesz wiedzieć, których selektorów użyć, aby Twoje testy były stabilne i czyste, mogę skierować Cię do jednego z moich artykułów, które dotyczą tego problemu. Sami programiści Cypress dostarczają wskazówek na ten temat w swoich najlepszych praktykach.

Nasz test jako sekwencja poleceń

OK, wróćmy do naszego testu. W nim chcemy wyświetlić nasz przepływ pracy:

„Ja, jako użytkownik, wyszukam artykuł autora i przejdę do witryny autora przez obszar referencyjny w jednym z jego artykułów”.

Odtworzymy kroki, które użytkownik wykonałby za pomocą poleceń. Poniżej wkleję gotowy test z komentarzami, które wyjaśnią kroki:

 // find-author.spec.js it('Find the author Ramona Schwering', () => { // Open the website cy.visit('https://www.smashingmagazine.com'); // Enter author's name in search field cy.get('#js-search-input').type('Ramona Schwering'); // Navigate to author's article cy.get('h2 > a').first().click(); // Open the author's page cy.get('.author-post__author-title').click(); });

Ten przykład dotyczy przepływu pracy, który chcemy przetestować. Cypress wykona ten test. Czy nadszedł czas, aby powiedzieć „gratulacje”? Czy w końcu skończyliśmy pisać nasz pierwszy test?

Cóż, proszę przyjrzyj się bliżej . Cypress wykona go, ale zrobi tylko to, co każe mu test, czyli to, co napisałeś. Jeśli uruchomisz go w biegaczu testowym, możesz sprawdzić, czy przeszedł – ale nie w przypadku, gdy przebiegłeś go bez głowy. Dzięki temu testowi wiemy tylko, czy Cypress mógł pomyślnie uruchamiać nasze polecenia — nie czy trafiliśmy na stronę autora. Musimy więc nauczyć nasz test, aby to ustalić.

Praca z twierdzeniami

Drugi typ instrukcji zajmuje się opisami pożądanego stanu interfejsu użytkownika — to znaczy, czy coś powinno istnieć, być widoczne, czy już nie być widoczne. Asercje w Cypress są oparte na asercjach Chai i Sinon-Chai, co jest widoczne w składni.

Pamiętaj, że chcemy sprawdzić, czy jesteśmy na stronie profilowej autora — w tym przypadku mojej. Tak więc musimy dodać asercję dokładnie w tym celu:

 // find-author.spec.js it('Find the author Ramona Schwering', () => { // Open the website cy.visit('https://www.smashingmagazine.com'); // Enter author's name in search field cy.get('#js-search-input').type('Ramona Schwering'); // Navigate to author's article cy.get('h2 > a').first().click(); // Open the author's page cy.get('.author-post__author-title').click(); // Check if we're on the author's site cy.contains('.author__title', 'Ramona Schwering').should('be.visible'); });

W porządku, teraz napisaliśmy test, który ma wartość. A więc tak, gratuluję napisania pierwszego testu… nawet jeśli nie jest jeszcze doskonały.

Zróbmy nasz test ładny

Nawet jeśli udałoby nam się napisać pierwszy sensowny test i nauczyliśmy się w tym procesie podstawowej koncepcji, nie scaliłbym jeszcze tego, gdyby został zaproponowany w żądaniu ściągnięcia. Pozostało jeszcze kilka rzeczy do zrobienia, aby zabłysnąć.

Nie spiesz się

Cypress ma wbudowaną opcję ponawiania w prawie każdym poleceniu, więc nie musisz czekać, aby zobaczyć, czy na przykład element już istnieje. Jednak to tylko sprawdza, czy element istnieje w DOM, a nie więcej. Cypress nie może przewidzieć wszystkiego, co robi Twoja aplikacja, więc może wystąpić pewna niestabilność, jeśli polegasz wyłącznie na tym.

Czas w testach end-to-end
Czas jest ważnym aspektem w testach end-to-end. (duży podgląd)

Co zrobiłby użytkownik, gdyby chciał zobaczyć witrynę, która wciąż się ładuje? Najprawdopodobniej czekałyby, aż niektóre części witryny staną się widoczne (a więc załadowane), a następnie wejdą z nimi w interakcję. W naszym teście chcemy dokładnie to naśladować: chcemy poczekać na zmiany w interfejsie użytkownika przed rozpoczęciem interakcji . W większości przypadków ograniczylibyśmy to zachowanie do elementów, których potrzebujemy, używając w ten sposób asercji na tych elementach.

Jak widać, musimy kilka razy sprawić, by nasz test czekał. Jednak czekanie zbyt wiele razy też nie jest dobre. Z reguły sugeruję użycie asercji, aby sprawdzić, czy element, z którym ma nastąpić interakcja, został w pełni załadowany, jako pierwszy krok do ustalenia, czy testowana witryna została załadowana.

Spójrzmy na taką część naszego testu jako przykład. Dodałem jedno potwierdzenie, aby upewnić się, że nasza strona została w pełni załadowana :

 // find-author-assertions.spec.js // Open website cy.visit('https://www.smashingmagazine.com'); // Ensure site is fully loaded cy.get('.headline-content').should('be.visible'); // Enter author's name in the search field cy.get('#js-search-input').type('Ramona Schwering');

Dodawaj asercje w taki sposób we wszystkich przypadkach, w których nasza strona internetowa będzie miała czasy ładowania lub kilka elementów, które trzeba renderować od nowa. Aby uzyskać pełny plik testowy, spójrz na odpowiedni test w repozytorium GitHub.

Aby uniknąć wpadnięcia w pułapkę niestabilnych testów, chciałbym dać ci ostatnią wskazówkę: Nigdy nie używaj stałych czasów oczekiwania, takich jak cy.wait(500) lub tym podobne.

Odpowiedzi API to Twoi znajomi

W szczególności jest jedna fajna możliwość oczekiwania, którą uwielbiam wykorzystywać w moich testach. W Cypress możliwa jest praca z funkcjami sieciowymi — innym pomocnym sposobem oczekiwania w aplikacji jest użycie tych funkcji do pracy z żądaniami sieciowymi . W ten sposób możesz sprawić, by test czekał na pomyślną odpowiedź API.

Jeśli pamiętamy nasz przepływ pracy jako przykład, jeden krok może świetnie wykorzystać możliwość oczekiwania na API. Myślę o wyszukiwaniu. Odpowiednia historyjka użytkownika może wyglądać następująco:

„Jako programista chcę mieć pewność, że nasze wyniki wyszukiwania zostały w pełni załadowane, aby żaden artykuł ze starszymi wynikami nie wprowadzał w błąd naszego testu”.

Zastosujmy to do naszego testu. Przede wszystkim musimy zdefiniować trasę, na którą chcemy później czekać. W tym celu możemy użyć polecenia intercept . Szukałbym żądania, przynosząc potrzebne mi dane — w tym przypadku wyniki wyszukiwania.

Aby ten przykład był prosty, użyję symbolu wieloznacznego dla adresu URL. Następnie użyję aliasu, aby Cypress mógł później pracować z tą trasą.

 // find-author-hooks.spec.js // Set the route to work with it('Find the author Ramona Schwering', () => { // Route to wait for later cy.intercept({ url: '*/indexes/smashingmagazine/*', method: 'POST' }).as('search'); // With this alias Cypress will find the request again //...

W Cypress wszystkie zdefiniowane trasy są wyświetlane na początku testu. Dlatego też chciałbym umieścić te polecenia intercept na początku mojego testu.

Trasa API w programie testowym Cypress
Trasa API zostanie wyświetlona na górze naszego testu. (duży podgląd)

Teraz możemy użyć tego aliasu trasy w asercjach. Najłatwiejszym sposobem na to byłoby użycie polecenia wait Cypressa, bezpośrednio z aliasem wspomnianym wcześniej. Jednak samo użycie tego polecenia prowadziłoby do oczekiwania na odpowiedź niezależnie od jej wyniku . Nawet kody błędów, takie jak 400 lub 500, będą liczone jako przejście, podczas gdy Twoja aplikacja najprawdopodobniej się zepsuje. Zalecam więc dodanie kolejnego asercji w ten sposób:

 // find-author-hooks.spec.js // Later: Assertion of the search request's status code cy.wait('@search') .its('response.statusCode').should('equal', 200);
Asercja dotycząca odpowiedzi API
Asercja dotycząca odpowiedzi API, jak widać w programie uruchamiającym testy Cypress. (duży podgląd)

W ten sposób możemy precyzyjnie czekać na dane oprogramowania, zmiany itp., nie tracąc czasu i nie wpadając w kłopoty, jeśli aplikacja jest mocno obciążona. Ponownie możesz znaleźć kompletny przykładowy plik w moim repozytorium GitHub.

Konfiguracja cyprysu

Pominąłem jeden mały szczegół. Jeśli przyjrzysz się bliżej kompletnemu przykładowi testowemu, różni się on nieco od tych, których użyliśmy w tym przewodniku.

 // Cypress describe('Find author at smashing', () => { beforeEach(() => { // Open website cy.visit('https://www.smashingmagazine.com'); }); //...

Używam tylko ukośnika, aby otworzyć stronę Smashing Magazine. Jak to działa? Cóż, użycie tego polecenia w ten sposób spowoduje przejście do baseUrl naszych testów. baseUrl to wartość konfiguracyjna, której można użyć jako przedrostka adresu URL polecenia cy.visit() lub cy.request() . Między innymi taką wartość możemy zdefiniować w pliku cypress.json . Na potrzeby naszego testu ustawimy baseUrl w następujący sposób:

 // cypress.json { "baseUrl": "https://www.smashingmagazine.com" }

Wyróżnienie: Haki

Pozostał jeszcze jeden temat, o którym chciałbym wspomnieć, nawet jeśli nasz przykładowy test nie jest odpowiedni do jego użycia. Jak to zwykle bywa w innych frameworkach testowych, możemy zdefiniować, co dzieje się przed i po naszych testach za pomocą tak zwanych hooków cyklu życia . Dokładniej, istnieją one po to, aby wykonać kod przed lub po jednym lub wszystkich testach:

 // Cypress describe('Hooks', function() { before(() => { // Runs once before all tests }); after(() => { // Runs once after all tests }); beforeEach(() => { // Runs before each test }); afterEach(() => { // Runs after each test }); });

Chcemy wypełnić nasz plik testowy więcej niż jednym testem, więc powinniśmy poszukać typowych kroków, które chcemy wykonać przed lub po nich. Nasz pierwszy wiersz jest dobrym przykładem, jest poleceniem visit . Zakładając, że chcemy otworzyć tę witrynę przed każdym z tych testów, przed każdym z beforeEach w naszym przykładzie wyglądałoby to tak:

 // Cypress describe('Find author at smashing', () => { beforeEach(() => { // Open website cy.visit('https://www.smashingmagazine.com'); }); //... 
przedKażdym hakiem
Hak beforeEach jest wyświetlany w dzienniku biegacza testowego. (duży podgląd)

Często używam tego w mojej codziennej pracy, aby na przykład upewnić się, że moja aplikacja zostanie zresetowana do stanu domyślnego przed testem , izolując w ten sposób test od innych testów. ( Nigdy nie polegaj na poprzednich testach! ) Uruchamiaj testy w izolacji od siebie, aby zachować kontrolę nad stanem aplikacji.

Każdy test powinien działać samodzielnie — niezależnie od innych testów. Ma to kluczowe znaczenie dla zapewnienia prawidłowych wyników testów . Aby uzyskać szczegółowe informacje na ten temat, zobacz sekcję „Dane, które udostępnialiśmy” w jednym z moich ostatnich artykułów. Na razie zapoznaj się z pełnym przykładem na GitHub, jeśli chcesz zobaczyć cały test.

Wniosek

Moim zdaniem testy end-to-end są niezbędnym elementem CI, utrzymującym jakość aplikacji na wysokim poziomie i jednocześnie odciążającym pracę testerów. Cypress to moje ulubione narzędzie do debugowania testów end-to-end szybko, stabilnie i wydajnie oraz do uruchamiania ich równolegle z dowolnym pull requestem w ramach CI. Krzywa uczenia się jest łagodna, jeśli znasz już JavaScript.

Mam nadzieję, że byłem w stanie cię trochę poprowadzić i dać ci punkt wyjścia do pisania testów Cypress i kilka praktycznych wskazówek, jak zacząć. Oczywiście wszystkie przykłady kodu są dostępne w repozytorium GitHub, więc zachęcamy do zapoznania się.

Oczywiście to tylko punkt wyjścia; jest wiele innych rzeczy do nauczenia się i przedyskutowania w związku z testami Cypress — zostawię ci kilka sugestii, czego możesz się nauczyć dalej. Mając to na uwadze, życzę miłego testowania!

Zasoby

  • Oryginalny przykład rozbijania, Ramona Schwering
    Repozytorium GitHub dla przykładu w tym artykule.
  • Dokumentacja cyprysowa
  • „Przepisy”, Cyprys
    Wybór przykładów, przepisów i kursów.
  • „Naucz się kodować za pomocą JavaScript: Cypress” (lekcja), CodeLikeThis
  • Najlepsze praktyki dotyczące pisania kompleksowych testów”, Shopware Docs