Praktyki budowania user stories w testach automatycznych

01.04.2019 | Michał Larysz

Dwa słowa wstępu

Projektując testy automatyczne dla interfejsu użytkownika możemy się posłużyć frameworkami, które wpisują się swoją strukturą w metodykę BDD. To właśnie w takich frameworkach jak cucumber czy JBehave możemy tworzyć historyjki użytkownika (user stories) do definiowania przebiegu testu automatycznego. Historyjka taka napisana jest w języku Gherkin, który ma określoną strukturę a odpowiednie słowa kluczowe określają jego elementy. Siadając do pisania swojej pierwszej historyjki posługiwałem się przykładami zaczerpniętymi z tutoriali, modyfikując je pod własny projekt.

Po jakimś czasie doszedłem do wniosku, że nie wystarcza mi to i muszę znaleźć efektywniejszy sposób opisywania przypadków testowych bo w projekcie robi się coraz większy bałagan. Tym artykułem chciałbym przekonać osoby dopiero zaczynające pracę z historyjkami użytkownika do praktyk wypracowanych w naszym zespole, a tych co już znają temat do przemyślenia swojego podejścia. Mam nadzieję, że pozwolą wam zaoszczędzić czas, który straciłem na kilkukrotnych modyfikacjach swojego projektu, zanim doszedłem do ostatecznego rozwiązania.

Z czym to się je?

Posługując się składnią wykorzystywaną we frameworku JBehave chciałbym wam przybliżyć tematykę budowania user stories dla testów automatycznych. Na początek musimy poznać strukturę historyjki oraz słowa kluczowe (pogrubione słowa) wykorzystywane do definiowania jej elementów:

Podstawowymi elementami są:

  • Sekcja Narrative to biznesowy opis historyjki (sekcja nie jest obowiązkowa).

    Przykład

    Narrative:
    In order to przekazania pieniędzy
    As a użytkownik platformy bankowej
    I want to wykonać przelew krajowy
    
  • Sekcja Scenario reprezentuje konkretny przypadek zachowania systemu (scenariusz). Jest zbiorem kroków, które są wykonywane przez framework testowy.

    Given – wstępny warunek zdarzenia
    When – wystąpienie zdarzenia
    Then – wynik zdarzenia

    Przykład

    Scenario: Wykonanie przelewu krajowego
    
    Given zalogowany użytkownik user1
    When wybierz z menu Przelew krajowy
    Then wyświetlona formatka Przelewu krajowego
    When wpisz w pole Numer rachunku wartość 12312312312312312312312312
    And wpisz w pole Kwota wartości 100,00
    And wpisz w pole Tytuł przelewu wartość Przekaż środki
    And kliknij przycisk Wyślij
    Then formatka przelewu krajowego zamyka się
    

W JBehave zostało wprowadzone jeszcze jedno słowo kluczowe And, które ma takie samo znaczenie jak słowo kluczowe występujące w kroku poprzedzającym.

Jedna historyjka (plik z rozszerzeniem .story) zawiera jeden lub więcej scenariuszy. Każdy z nich może zawierać wiele kroków opisanych tym samym słowem kluczowym. Przeważnie jeden scenariusz zawiera co najmniej po jednym typie słowa kluczowego Given, When, Then.

A teraz czas na konkrety...

Struktura frameworku jest tak zaprojektowana aby pojedyncza linijka historyjki (krok) była mapowana na jedną metodę w kodzie. Zatem poszczególny krok powinien być reprezentowany przez pojedynczą akcję. Oczywiście nic nie stoi na przeszkodzie, aby zrobić to inaczej i cały test automatyczny zawrzeć w jednym kroku.

Zerknijmy na poniższe przykłady:

Given zalogowany użytkownik wypełnił formatkę i wysłał przelew, który zapisał się w historii przelewów

Pod kątem biznesowym krok jest prawidłowy, natomiast nie jest reużywalny, a przecież nie chcemy pisać nowych kroków dla każdej nowej akcji tylko wykorzystywać już istniejące.

W kolejnym przykładzie jedna akcja jest reprezentowana przez więcej niż jeden krok:

Given otwarta formatka przelewu krajowego
When wyczyść pole Kwota
And wpisz w pole Kwota wartości 100,00
And ...

Widzimy, że krok, który powoduje wyczyszczenie dotychczasowej wartości z pola kwota jest nadmiarowy i może zostać zintegrowany z tym, który wpisuje w to pole wartość. Każda zmiana kwoty będzie się wiązała z wyczyszczeniem poprzedniej wartości, a wpisując kwotę do pustego pola takie czyszczenie nie zaszkodzi. W ten sposób będziemy mieli obsłużone wszystkie sytuacje.

Rozpatrując powyższe przykłady łatwo sobie uświadomić, że należy wybrać złoty środek przy definiowaniu kroków, aby nie przesadzić zarówno w jedną jak i w drugą stronę. Oczywiście wybór sposobu definiowania kroków jest sprawą indywidualną i wymaga dopasowania do samego projektu.

Mój przepis

Najlepiej jest tak zdefiniować granulację kroków, aby odpowiadały pojedynczym czynnościom, które wykonuje użytkownik systemu:

Kliknij na przycisk
Wpisz do pola
Wybierz z listy
Wczytaj plik

Różne osoby używają innych konstrukcji definiując historyjki użytkownika. Jedno zdanie o tym samym znaczeniu można zapisać na kilka różnych sposobów, więc warto określić sobie schemat budowania kroków w historyjce, tak aby po napisaniu całego testu wymagane były tylko niewielkie poprawki. Przede wszystkim aby nie tworzyć kroków, które robią to samo a brzmią podobnie.

Jeżeli piszemy historyjki w środowisku programistycznym (IntelliJ IDEA, Eclipse itp.) i używamy wtyczki dla danego frameworka to możemy wyświetlić sobie podpowiedzi dopasowane do wpisanych słów (najczęściej klikając CTRL + spacja). Kiedy w naszym projekcie mamy już kilkadziesiąt lub kilkaset historyjek a liczbę kroków można liczyć w setkach to bardzo trudno wyszukać krok, który moglibyśmy użyć ponownie jeżeli nie określimy standardu budowania kroków.

Jednym z pomysłów może być zapisywanie słów w zdaniach w następujący sposób:

[czynność] [element] [wartość]

Dzięki temu wiadomo, że przy poszukiwaniu odpowiedniego kroku będziemy szukali najpierw czynności, która w nim występuje.

Kiedy baza naszych testów się rozrasta mamy coraz więcej podobnych kroków. Powoduje to, że osoby zajmujące się implementacją testów zaczynają się gubić w tym jaki krok należy wykorzystać do jakiej formatki. Aby doprecyzować kontekst wykonywanej akcji możemy rozszerzyć definicję naszych kroków poprzez dodanie słów „w sekcji…”, „w oknie…”, „na formatce...”:

When wpisz w pole Kwota wartość 100,00 na formatce Przelew krajowy

Czytając taką historyjkę wiemy o jaki obiekt nam chodzi i gdzie dokładnie jest zlokalizowany.

Ze swojej praktyki wiem, że dużo lepiej zastosować poniższy szablon:

[kontekst]: [czynność] [element] [wartość]

Co przekłada się na:

When Przelew krajowy: wpisz w pole Kwota wartość 100,00

Ten zapis jest krótszy a poza tym niesie jeszcze jedną korzyść. Przy wyszukiwaniu kroków poprzez plugin do IDE możemy wylistować wszystkie kroki z danej sekcji, modułu lub okna, ponieważ plugin podpowiada kroki poprzez dopasowanie do pierwszych wpisanych słów. Lista dostępnych kroków zawęzi nam się z kilkudziesięciu do kilkunastu a nawet kilku.

Zobaczmy zatem jak po modyfikacjach mógłby wyglądać scenariusz zaprezentowany na początku materiału:

Scenario: Wykonanie przelewu krajowego

Given Logowanie: zalogowany użytkownik user1
When Główna: wybierz z menu Przelew krajowy
Then Główna: wyświetlona formatka Przelewu krajowego
When Przelew krajowy: wpisz w pole Numer Rachunku wartość 12312312312312312312312312
And Przelew krajowy: wpisz w pole Kwota wartości 100,00
And Przelew krajowy: wpisz w pole Tytuł przelewu wartość Przekaż środki
And Przelew krajowy: kliknij przycisk Wyślij
Then Główna: formatka przelewu krajowego zamyka się

W ten sposób mamy pogrupowane kroki w zależności od kontekstu użycia. Praca w takim schemacie jest dużo łatwiejsza dla osoby piszącej scenariusze, gdyż może ona w łatwy sposób przeszukiwać grupy kroków w poszukiwaniu właściwego. Oprócz tego ułatwia to pracę osobie implementującej akcje w frameworku ze względu na modularność rozwiązania, które bardzo dobrze wpisuje się w Page Object Pattern.

To jeszcze nie koniec...

Przedstawiłem wam tylko jedno zagadnienie, ale moim zdaniem najważniejsze, które z powodzeniem jest wykorzystywane w frameworku, z którym pracuję. Postaram się napisać kolejne artykuły, które przybliżą wam sprawdzone praktyki ułatwiające pracę z historyjkami użytkownika w frameworkach do testów automatycznych.