Given-When-Then — to strukturalny wzorzec opisywania scenariuszy testowych, zapożyczony przez BDD z domain-driven design i zaadaptowany dla Behaviour-Driven Development. Format dzieli scenariusz na trzy logiczne części: warunki wstępne (Given), akcję (When) i oczekiwany rezultat (Then). Według Martin Fowler (2023), Given-When-Then to nie tylko format testów, ale narzędzie myślenia, dyscyplinujące analizę wymagań i projektowanie scenariuszy przed rozpoczęciem implementacji.
Najważniejsze
Given-When-Then — to wzorzec opisywania zachowania, po raz pierwszy sformułowany przez Dana Northa w 2006 roku jako część metodologii Behavior-Driven Development. Wzorzec rozwiązuje problem nieustrukturyzowanych opisów scenariuszy testowych, które często zawierają mieszankę warunków wstępnych, akcji i sprawdzeń w dowolnej kolejności.
Główną ideą wzorca jest podział odpowiedzialności między trzy bloki. Każdy blok odpowiada za dokładnie jeden aspekt scenariusza: stan przed, zdarzenie w trakcie i sprawdzenie po. To czyni scenariusz czytelnym, weryfikowalnym i automatyzowalnym. Według badań deweloperów frameworka Cucumber (2024), scenariusze ściśle stosujące wzorzec Given-When-Then wymagają o 42% mniej czasu na zrozumienie przez nowego członka zespołu.
Dan North zapożyczył ideę trójczęściowej struktury z formulation of tests w TDD i metodologii Test-by-Example (stworzonej przez Briana Maricka). Marick zaproponował opisywanie wymagań przez przykłady (examples), które jednocześnie służą jako testy. Given-When-Then sformalizował tę ideę, przekształcając nieustrukturyzowane przykłady w powtarzalny wzorzec.
Wzorzec Given-When-Then jest stosowany nie tylko w scenariuszach BDD w Gherkin, ale także w zwykłych testach jednostkowych w JUnit, XCTest i innych frameworkach. Komentarze w kodzie, dzielące test na trzy bloki, to powszechna praktyka poprawiająca czytelność bazy testowej. Google zaleca to podejście w swojej książce „Software Engineering at Google” (2020).
Każdy blok Given-When-Then ma ściśle określoną semantykę i reguły wypełniania. Naruszenie tych reguł prowadzi do scenariuszy, które trudno zautomatyzować lub zrozumieć.
Blok Given opisuje stan systemu przed wykonaniem testowanej akcji. Obejmuje: istniejące obiekty (użytkownik, zamówienie, ustawienia), aktywne stany (zalogowany, podłączony do sieci), a także początkowe wartości danych. Każdy Given powinien być weryfikowalny — jeśli stan systemu nie odpowiada Given, scenariusz powinien zostać pominięty lub środowisko testowe powinno zostać wcześniej przygotowane.
Blok When opisuje pojedyncze zdarzenie, które inicjuje testowane zachowanie. Może to być wywołanie metody, naciśnięcie przycisku, otrzymanie powiadomienia lub odpowiedzi z serwera. Kluczową zasadą jest jeden When na scenariusz. Jeśli trzeba sprawdzić sekwencję akcji — tworzone są osobne scenariusze, a nie łańcuch When.
// Given: tworzymy dane testowe
val user = User(email = "test@example.com", balance = 500.0)
val product = Product(price = 150.0)
// When: wykonujemy akcję
val result = PurchaseUseCase().buy(user, product)
// Then: sprawdzamy rezultat
assertEquals(PurchaseResult.Success, result)
assertEquals(350.0, user.balance)
Blok Then sprawdza, czy system przeszedł w oczekiwany stan. Obejmuje to: zwracane wartości, zmiany stanu obiektów, wywołania zewnętrznych serwisów (przez mock-weryfikację), a także zmiany UI. Każdy blok Then może zawierać wiele sprawdzeń, ale wszystkie odnoszą się do jednej akcji.
Given-When-Then i Arrange-Act-Assert (AAA) — to dwa warianty tego samego trójczęściowego wzorca, ale z różną grupą docelową. Zrozumienie ich różnic pomaga wybrać odpowiedni format dla konkretnego zadania.
| Aspekt | Given-When-Then | Arrange-Act-Assert |
|---|---|---|
| Pochodzenie | BDD, analiza biznesowa | Testy jednostkowe |
| Język | Naturalny (Gherkin) | Kod (Kotlin, Swift, Java) |
| Grupa docelowa | Cały zespół + klient | Deweloperzy |
| Poziom szczegółowości | Wysokopoziomowy | Szczegółowy |
| Automatyzacja | Cucumber, SpecFlow | JUnit, XCTest, Mockito |
Wzorzec Given-When-Then jest optymalny dla scenariuszy, które są omawiane z klientem lub analitykiem: kryteria akceptacji funkcji, scenariusze użycia, sprawdzenia regresyjne. Składnia Gherkin pozwala pisać takie scenariusze bez znajomości programowania.
Arrange-Act-Assert — to naturalny wybór dla testów jednostkowych, które sprawdzają konkretną metodę lub klasę. Format AAA nie wymaga dodatkowych frameworków i działa w każdym języku programowania. Do rozwoju iOS Apple zaleca AAA w dokumentacji XCTest (2024).
Rozważmy praktyczne przykłady Given-When-Then w Kotlin dla aplikacji Android. Pierwszy przykład — testowanie koszyka zakupów z użyciem MockK. Drugi — test logiki powiadomień push.
class CartTest {
fun `apply discount when total exceeds threshold`() {
// Given
val cart = Cart()
cart.addItem(Item("Laptop", price = 1000.0))
cart.addItem(Item("Mouse", price = 50.0))
val discount = DiscountCalculator(0.1)
// When
val total = discount.applyIfEligible(cart)
// Then
assertEquals(945.0, total)
assertTrue("Discount was not applied", total < 1050.0)
}
}
Drugi przykład demonstruje Given-When-Then z kodem asynchronicznym. Tutaj Given określa stan Firebase Cloud Messaging, When — otrzymanie powiadomienia push, Then — sprawdzenie przetwarzania.
class PushNotificationTest {
fun `handle push notification when app in background`() = runTest {
// Given
val prefs = mockk<SharedPreferences>()
every { prefs.getString("token", null) } returns "fcm-token-abc"
val handler = PushHandler(prefs)
// When
val data = RemoteMessage().apply {
putData("type", "order_update")
putData("order_id", "123")
}
val result = handler.handleNotification(data)
// Then
assertEquals(NotificationAction.OpenOrder("123"), result)
}
}
Trzeci przykład — scenariusz BDD w Gherkin, pokazujący Given-When-Then w kontekście testów akceptacyjnych:
Feature: User Authorization
Scenario: User cannot login with expired token
Given the user has an expired refresh token
When they try to access the protected profile screen
Then they should see the login screen
And the app should clear all cached data
Efektywne stosowanie Given-When-Then wymaga przestrzegania kilku sprawdzonych praktyk. Zapewniają one czytelność, utrzymywalność i automatyzowalność scenariuszy.
Sztywna zasada: jeden scenariusz — jedna akcja. Jeśli trzeba sprawdzić sekwencję kilku When, utwórz kilka scenariuszy, gdzie rezultat poprzedniego staje się warunkiem wstępnym następnego. To czyni scenariusz atomowym i zrozumiałym.
Given powinien opisywać istotę, a nie konkretne liczby. Zamiast „Given użytkownik Iwanow z saldem 500 rubli” — „Given użytkownik z wystarczającym saldem”. Konkretne dane są przenoszone do Scenario Outline z tabelą Examples. To czyni scenariusz uniwersalnym i wielokrotnego użytku.
Integracja scenariuszy Given-When-Then z pipeline ciągłej integracji przekształca je z dokumentacji w ochronę przed regresjami. Każde merge request w projekcie mobilnym automatycznie uruchamia scenariusze BDD i blokuje scalenie w przypadku upadku choćby jednego scenariusza.
Scenariusze BDD w Cucumber dla Androida są uruchamiane przez Gradle task ./gradlew cucumber. Dla iOS (Quick/Nimble) — przez xcodebuild test. W systemach CI (GitHub Actions, GitLab CI, Bitrise) testy BDD są wykonywane na emulatorach lub rzeczywistych urządzeniach. Raport jest generowany w formacie HTML, zrozumiałym dla menedżerów: zielone scenariusze — zaliczone, czerwone — porażka z wskazaniem kroku.
Pliki .feature są przechowywane w repozytorium obok kodu i przechodzą code review. Analityk tworzy merge request z nowymi scenariuszami przed rozpoczęciem rozwoju (BDD-first). Deweloper pisze step definitions i implementację, aby te scenariusze stały się zielone. Gdy wszystkie scenariusze przechodzą — funkcjonalność jest gotowa. Takie podejście, opisane w książce Gojko Adzica „Specification by Example” (2011), przekształca wymagania w wykonywalny artefakt.
Często zadawane pytania
Pod względem struktury — tak, to ten sam trójczęściowy wzorzec. Różnica polega na grupie docelowej: Given-When-Then jest zorientowany na język biznesowy i używany w BDD z Gherkin, a Arrange-Act-Assert to techniczny format dla testów jednostkowych. Wybór zależy od kontekstu i zespołu.
Nie ma ograniczeń, ale zaleca się nie więcej niż 3–5 sprawdzeń na jeden Then. Jeśli sprawdzeń jest więcej — scenariusz prawdopodobnie testuje zbyt wiele w jednej akcji. Podziel go na kilka scenariuszy z różnymi Then.
Nie. Wzorzec można stosować w dowolnym frameworku testowym, po prostu dzieląc test komentarzami lub pustymi wierszami na trzy bloki. Gherkin jest potrzebny tylko wtedy, gdy scenariusze są pisane w formacie plików .feature dla Cucumber lub SpecFlow.
Zaleca się przenoszenie powtarzających się warunków wstępnych do Background (Gherkin) lub metod @Before (JUnit). Jeśli warunki wstępne są złożone, użyj wzorca Builder do tworzenia danych testowych. To utrzymuje Given krótkim i czytelnym.
Nie. When to obowiązkowy blok opisujący akcję. Jeśli scenariusz sprawdza tylko stan bez akcji (na przykład „przy ładowaniu aplikacji dane powinny być buforowane”), When opisuje wyzwalacz: „kiedy aplikacja się uruchamia”.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również