Jak wybrać framework testowy do Behaviour-Driven Development?

21/12/2020

Data ostatniej aktualizacji: 14.01.2021 12:34

Wybór odpowiednich narzędzi do testów automatycznych w naszym projekcie jest ważnym elementem efektywnego procesu wytwórczego. Dlatego przy wyborze należy się zastanowić, czy jest ono faktycznie dobrze dopasowane do naszego sposobu pracy. Niewłaściwie dopasowany framework testowy może być przyczyną zwiększonego nakładu pracy z naszej strony. Skutkiem tego mogą być opóźnienia bądź zmniejszenie jakości dostarczanego oprogramowania.

W niniejszym artykule postaram się przedstawić przykłady frameworków dostępnych w ramach platformy wirtualnej maszyny Javy wspierających testy w projekcie wykorzystującym Behaviour-Driven Development (BDD). Nie będzie to dogłębna analiza poszczególnych narzędzi. Pragnę w kilku zdaniach zaprezentować Wam mocne i słabe strony każdego z przedstawionych frameworków.
W swoim opisie skupię się na 3 elementach każdego z tych narzędzi:

Czym jest Behavior-Driven Development (BDD)?

Zanim przejdziemy do wyboru narzędzi testowych odpowiedzmy sobie czym jest BDD oraz jak wygląda jego wykorzystanie w praktyce.

BDD to proces wytwórczy skupiający swoją uwagę na wspólnej komunikacji biznesu oraz zespołu wytwórczego. Powstał z połączenia Test-Driven Development, Domain-Driven Design oraz Object-Oriented Analysis and Design. Jego celem jest zarządzanie procesem wytwarzania oprogramowania przez interesy biznesowe wsparte wiedzą techniczną zespołu oraz zaawansowanymi narzędziami do weryfikacji założeń biznesowych. Specyfikacja funkcjonalności opisana jest za pomocą składni języka naturalnego (zazwyczaj angielskiego) w połączeniu z językiem specyficznym dla danej dziedziny (domeny), której używa system.

BDD opiera swoje działanie na 4 krokach zaczerpniętych z metodyki Test-Driven Development, które musi wykonać programista lub zespół wytwórczy:

Warto zwrócić uwagę, że testy zdefiniowane w BDD opierają się na wysokopoziomowych wymaganiach, najczęściej biznesowych. Stąd też często proces jest modyfikowany i testy w formie używanej w BDD wykonywane są jako testy akceptacyjne.

Głównym znakiem rozpoznawalnym testów w BDD jest struktura przypadku testowego, która opiera się na słowach kluczowych Given… When… Then… co odpowiada strukturze historyjek użytkowników (User Stories) w postaci „jako [rola] chcę [opis oczekiwanej funkcji], aby [opis oczekiwanej korzyści]”.

Cucumber Framework logo

Framework testowy: CUCUMBER

Większość z nas po usłyszeniu BDD od razu pomyśli o frameworku Cucumber. Jest to obecnie jedno z najpopularniejszych narzędzi do testów BDD, nie tylko dla Javy, ale także dla Ruby’ego, JavaScriptu czy Kotlina.

Definicja scenariuszy

Jak wspominałem wcześniej jednym ze znaków rozpoznawalnych BDD, a zarazem zaletą, jest sposób definicji przypadków testowych. W Cucumber definicja scenariuszy odbywa się w składni Gherkin w plikach `.feature`. Przypadki są łatwe do zrozumienia dla nietechnicznych uczestników procesu wytwórczego, ponieważ definicja scenariusza jest zapisana za pomocą języka naturalnego.

Przykładowy test zapisany w języku Gherkin wygląda następująco:

Feature: Buy a book 
 
  Scenario: User adds book to the basket 
    Given User found interesting book 
    When user adds book to the basket 
    And user opens the basket 
    Then book is in basket  
    And basket sum is equal to $10 

Podczas uruchomienia poszczególne kroki mapowane są na zdefiniowane przez testera akcje, które symulują zachowanie użytkownika w testowanym systemie.

Techniczna strona Cucumber

W przypadku Cucumber konfiguracja do pierwszego uruchomienia jest bardzo łatwa. Najprostsza wymagana konfiguracja składa się z formatu, w jakim Cucumber ma zwracać nam wyniki oraz ścieżek przetrzymywania plików `.feature` w naszym projekcie. Kroki definiować możemy na dwa sposoby albo pisząc standardowe metody i oznaczając je adnotacją `@Given`, `@When` itd., albo używając składni lambda, definiując mapowanie nazwy kroku wewnątrz niej.

Definicja kroku w metodzie:

@Then("book is in basket") 
public void bookIsInBasket() { 
    basket.assertContainsAtLeastOneBook(); 
} 

Definicja kroku z użyciem wyrażenia lambda:

Then( 
    "book is in basket", 
    () -> { 
      basket.assertContainsAtLeastOneBook(); 
    });

Wsparcie dla podświetlania składni definicji kroków Cucumber oraz scenariuszy w Gherkin w Intellij wspierane jest za pomocą pluginów. Pojawiają się tutaj jednak małe niedogodności… Jedną z nich jest nawigacja pomiędzy scenariuszami oraz definicją kroków. W przypadku prostych kroków możemy bez problemu nawigować pomiędzy krokami w scenariuszu oraz ich definicjami. Nie jest to możliwe w przypadku wykorzystania najnowszej wersji definicji, czyli wyrażeń lambda. Przez co zmieniając definicję kroku, sami musimy się upewnić, gdzie była ona używana.

Drugim problemem, który mogą napotkać bardziej zaawansowani użytkownicy Cucumber, jest brak możliwości nawigacji w przypadku kroków używających własnoręcznie zdefiniowanych parametrów. Warto zwrócić też uwagę na to, jakie wymagania mamy do samego raportowania wyników. W Cucumber mamy domyślnie możliwość eksportowania wyników do HTML oraz JSON. Jednak w przypadku konieczności dostosowania projektu do narzuconego wzoru raportowego możemy mieć problemy.

Warto również rozważyć rozszerzenie możliwości raportowych korzystając z gotowych narzędzi generujących raporty z testów takich jak Allure. Dostarcza ono adapter dla Cucumber, dzięki czemu jego wykorzystanie jest jeszcze łatwiejsze. W wyniku swojej popularności ten framework testowy ma dużą społeczność użytkowników, która pomaga w rozwiązywaniu napotkanych problemów. Z drugiej strony natłok informacji może czasem znacząco utrudnić zadanie. Szukając rozwiązania napotkanego problemu, możemy napotkać na bardzo dużo informacji, jak coś można było zrobić w starszych wersjach. Utrudnia to znalezienie rozwiązania pasującego do wersji Cucumber obecnie przez nas używanej.

 

jbehave framework logo

Framework testowy: JBEHAVE

Kolejnym bardzo popularnym frameworkiem, od którego de facto wszystko się zaczęło, jest jbehave. Początek jbehave to rok 2003, kiedy to Daniel Terhorst-North rozpoczął pracę nad własnym frameworkiem dotyczącym definiowania zachowań w aplikacji. Wynikiem tego było powstanie „nurtu” BDD.

Definicja scenariuszy

Jbehave potrafi korzystać z plików zdefiniowanych za pomocą Gherkin, ale jego podstawowym sposobem definicji przypadków testowych jest własny syntax w tekstowych plikach `.story’.

Buy a book 
 
Scenario: User adds book to the basket 
 
Given User found interesting book 
When user adds book to the basket 
And user opens the basket 
Then book is in basket  
And basket sum is equal to $10 

Jak widać w podstawowym zakresie jest on prawie identyczny jak Gherkin. Jednak w przypadku konieczności napisania bardziej złożonych testów zyskuje on przewagę. Przykładem takiego wykorzystania może być definicja instrukcji wykonywanych po każdym kroku czy nawet po kroku, który został wykonany niepoprawnie. Więcej szczegółów na temat możliwości składni można znaleźć na stronie jbehave.

Techniczna strona jbehave

Konfiguracja Jbehave do pierwszego uruchomienia jest bardziej skomplikowana niż w przypadku Cucumber. Na pochwałę jednak zasługuje dokumentacja, która dobrze wyjaśnia i ułatwia zadania związane z rozwojem projektu. Identycznie jak w przypadku Cucumber – podświetlanie składni plików historyjek jest wspomagane pluginem. Nawigacja w projekcie używającego Jbehave pomiędzy scenariuszami oraz definicjami kroków jest identyczna jak w przypadku Cucumber, nie wliczając problemów z lambdami, które tutaj nie występują. W temacie raportów Jbehave daje dużo większe możliwości dostosowania raportu, tutaj również polecam integrację z Allure, które dostarcza adapter.

 

jGiven framework logo
Framework testowy: JGIVEN

Projekt JGiven swoją historię zaczyna około 2014 roku. Cel, jaki przyświeca projektowi to wyeliminowanie połączenia plików tekstowych i kodu w projektach BDD.

Definicja scenariuszy

Jak możemy się domyślić z pierwszego akapitu, podejście do pisania scenariuszy w JGiven różni się od tych z jbehave czy Cucumber. Cały kod scenariuszy napisany jest w Java, a nazwy metod działają tutaj jako scenariusz.

@Test 
public void user_adds_book_to_the_basket() { 
    given().user_found_interesting_book(); 
    when().user_adds_book_to_the_basket(). 
            and().user_opens_the_basket(); 
    then().book_is_in_the_basket(). 
            and().basket_sum_is_equal_to(10); 
}

Jak widzimy scenariusz tego typu jest mniej czytelny dla użytkowników nietechnicznych, niż te z poprzednich frameworków. Rozwiązanie przychodzi zaraz po uruchomieniu testów. Dzięki temu, że wyniki testu z użyciem JGiven prezentowane są jako scenariusze w składki Gherkin, pozwala to na prezentację wykonanych testów w przystępnej dla biznesu formie. Wynikowy scenariusz, który otrzymujemy po uruchomienia testu z użyciem JGiven.

User adds book to the basket 

   Given user found interesting book 
    When user adds book to the basket 
     And user opens the basket 
    Then book is in the basket 
     And basket sum is equal to 10 

Techniczna strona JGiven

W związku z tym, że cały projekt wraz ze scenariuszami napisany jest w Java, nie wymaga on żadnych dodatkowych pluginów w celu kolorowania składni czy nawigacji pomiędzy scenariuszami oraz definicją kroków. Podejście takie pozytywnie wpływa także na możliwość reużywalności kodu, w związku z tym, że naszymi krokami są zwykłe metody, możemy wywoływać je w dowolnym miejscu naszego projektu. W przypadku Cucumbera oraz jbehave jest to utrudnione ze względu na sposób definiowania kroków. Narzędzie JGiven ma bardziej rozbudowane możliwości prezentacji raportów od poprzedników. Już samo w sobie jest w stanie wygenerować raport w postaci przejrzystej strony HTML.

Które narzędzie jest dla mnie?

Narzędzia, które przedstawiłem są w czołówce popularności narzędzi do BDD na rynku. JGiven dodałem do zestawienia ze względu na zupełnie inne podejście do tego procesu. Chciałem nimi zaprezentować jednakowo różnorodność, ale także to, że gdy są one bardzo podobne nawet bardzo małe różnice, mogą wpływać na to, że lepiej lub gorzej pasują do naszego projektu.
Cucumber i jbehave wykorzystują język naturalny do definicji scenariuszy. Dzięki takiemu podejściu mogą one być pisane przez analityków biznesowych czy szeroko rozumiany biznes. Sprawdzą się np. w projektach, gdzie do definicji nowych funkcjonalności stosujemy historyjki użytkownika (User Stories).

Wybierając narzędzie, należy także wziąć pod uwagę techniczne aspekty obu rozwiązań. Mimo lekkich różnic we wsparciu naszego IDE oba narzędzia pod względem technicznym są podobne, cucumber może zachęcić osoby, które definicje kroków chcą zapisywać za pomocą wyrażeń lambda. Różnice jednak pojawią się gdy wyniki testów muszą być prezentowane w ściśle określonej formie, tutaj lepszym rozwiązaniem może być jbehave.

Co do ostatniego narzędzia JGiven, może ono wykorzystywać historyjki, które dostarczy nam biznes. Będą one jednak musiały być przepisane na kod Java. Stąd ten framework testowy sprawdzi się o wiele lepiej, jeżeli inicjatywa użycia BDD wyjdzie od zespołu wytwórczego, od strony testerów czy nawet developerów, gdyż to narzędzie może być jak najbardziej wykorzystywane w przypadku testów jednostkowych. Wyniki dzięki swojej strukturze będą czytelne dla nietechnicznych uczestników procesu wytwórczego.

Podsumowanie

Mam nadzieję, że ten krótki artykuł pomoże Wam zrozumieć, iż nie zawsze najpopularniejsze narzędzie jest tym najlepszym. Warto poświęcić dłuższą chwilę na wybór odpowiednich narzędzi, dobrze dopasowanych do naszych potrzeb. Czas ten z pewnością nie będzie stracony i przyczyni się do zaoszczędzenia nie tylko pieniędzy, ale i naszych nerwów w trakcie rozwoju projektu.

Karol Majchrzak, Starszy Tester Automatyzujący
Altkom Software & Consulting