Given-When-Then은 테스트 시나리오를 설명하기 위한 구조적 패턴으로, BDD가 domain-driven design에서 차용하여 Behaviour-Driven Development에 맞게 조정한 것입니다. 이 형식은 시나리오를 세 가지 논리적 부분(전제조건(Given), 동작(When), 예상 결과(Then))으로 나눕니다. Martin Fowler(2023)에 따르면, Given-When-Then은 단순한 테스트 형식이 아니라 구현을 시작하기 전에 요구사항 분석과 시나리오 설계를 체계화하는 사고 도구입니다.
핵심 요점
Given-When-Then은 Dan North가 2006년 Behavior-Driven Development 방법론의 일부로 처음 공식화한 동작 설명 패턴입니다. 이 패턴은 임의의 순서로 전제조건, 동작 및 검증이 혼합되어 있는 경우가 많은 비구조화된 테스트 시나리오 설명의 문제를 해결합니다.
패턴의 주요 아이디어는 세 블록 간의 책임 분리입니다. 각 블록은 시나리오의 정확히 한 가지 측면(이전 상태, 이벤트 중, 이후 검증)을 담당합니다. 이렇게 하면 시나리오를 읽고, 검증하고, 자동화할 수 있습니다. Cucumber 프레임워크 개발자의 연구(2024)에 따르면, Given-When-Then 패턴을 엄격히 따르는 시나리오는 새 팀원이 이해하는 데 42% 더 적은 시간이 소요됩니다.
Dan North는 TDD의 테스트 공식화와 Test-by-Example 방법론(Brian Marick이 창안)에서 세 부분으로 구성된 구조의 아이디어를 차용했습니다. Marick은 동시에 테스트 역할을 하는 예제( examples)를 통해 요구사항을 설명할 것을 제안했습니다. Given-When-Then은 이 아이디어를 공식화하여 비구조화된 예제를 반복 가능한 패턴으로 전환했습니다.
Given-When-Then 패턴은 Gherkin의 BDD 시나리오뿐만 아니라 JUnit, XCTest 및 기타 프레임워크의 일반적인 단위 테스트에도 적용됩니다. 테스트를 세 블록으로 나누는 코드 내 주석은 테스트 베이스의 가독성을 향상시키는 일반적인 관행입니다. Google은 저서 『Software Engineering at Google』(2020)에서 이 접근 방식을 권장합니다.
Given-When-Then의 각 블록에는 엄격하게 정의된 의미와 작성 규칙이 있습니다. 이러한 규칙을 위반하면 자동화하거나 이해하기 어려운 시나리오가 생성됩니다.
Given 블록은 테스트 대상 동작을 실행하기 전 시스템의 상태를 설명합니다. 여기에는 기존 객체(사용자, 주문, 설정), 활성 상태(인증됨, 네트워크 연결됨) 및 데이터의 초기 값이 포함됩니다. 각 Given은 검증 가능해야 합니다. 시스템 상태가 Given과 일치하지 않으면 시나리오를 건너뛰거나 테스트 환경을 미리 준비해야 합니다.
When 블록은 테스트 대상 동작을 시작하는 단일 이벤트를 설명합니다. 메서드 호출, 버튼 클릭, 알림 수신 또는 서버 응답일 수 있습니다. 핵심 규칙은 시나리오당 하나의 When입니다. 동작 시퀀스를 확인해야 하는 경우 When 체인이 아니라 별도의 시나리오를 만듭니다.
// Given: 테스트 데이터 생성
val user = User(email = "test@example.com", balance = 500.0)
val product = Product(price = 150.0)
// When: 동작 실행
val result = PurchaseUseCase().buy(user, product)
// Then: 결과 검증
assertEquals(PurchaseResult.Success, result)
assertEquals(350.0, user.balance)
Then 블록은 시스템이 예상 상태로 전환되었는지 확인합니다. 여기에는 반환 값, 객체 상태 변경, 외부 서비스 호출(모크 검증을 통해) 및 UI 변경이 포함됩니다. 각 Then 블록에는 여러 검증이 포함될 수 있지만 모두 단일 동작과 관련됩니다.
Given-When-Then과 Arrange-Act-Assert(AAA)는 동일한 세 부분 패턴의 두 가지 변형이지만 대상 사용자가 다릅니다. 차이점을 이해하면 특정 작업에 적합한 형식을 선택하는 데 도움이 됩니다.
| 측면 | Given-When-Then | Arrange-Act-Assert |
|---|---|---|
| 기원 | BDD, 비즈니스 분석 | 단위 테스트 |
| 언어 | 자연어(Gherkin) | 코드(Kotlin, Swift, Java) |
| 대상 | 전체 팀 + 클라이언트 | 개발자 |
| 세부 수준 | 높은 수준 | 상세 |
| 자동화 | Cucumber, SpecFlow | JUnit, XCTest, Mockito |
Given-When-Then 패턴은 클라이언트나 분석가와 논의하는 시나리오(기능의 승인 기준, 사용 사례, 회귀 검사)에 최적입니다. Gherkin 구문을 사용하면 프로그래밍 지식 없이도 이러한 시나리오를 작성할 수 있습니다.
Arrange-Act-Assert는 특정 메서드나 클래스를 검증하는 단위 테스트의 자연스러운 선택입니다. AAA 형식은 추가 프레임워크가 필요하지 않으며 모든 프로그래밍 언어에서 작동합니다. iOS 개발의 경우 Apple은 XCTest 문서(2024)에서 AAA를 권장합니다.
Android 애플리케이션용 Kotlin의 Given-When-Then 실제 예제를 살펴보겠습니다. 첫 번째 예제는 MockK을 사용한 쇼핑 카트 테스트입니다. 두 번째는 푸시 알림 로직 테스트입니다.
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)
}
}
두 번째 예제는 비동기 코드를 사용한 Given-When-Then을 보여줍니다. 여기서 Given은 Firebase Cloud Messaging의 상태를 설정하고, When은 푸시 알림 수신, Then은 처리 검증을 수행합니다.
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)
}
}
세 번째 예제는 승인 테스트 컨텍스트에서 Given-When-Then을 보여주는 Gherkin의 BDD 시나리오입니다:
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
Given-When-Then을 효과적으로 적용하려면 몇 가지 검증된 관행을 따라야 합니다. 이러한 관행은 시나리오의 가독성, 유지 관리 용이성 및 자동화를 보장합니다.
엄격한 규칙: 하나의 시나리오 — 하나의 동작. 여러 개의 When 시퀀스를 확인해야 하는 경우 이전 결과가 다음의 전제 조건이 되는 여러 시나리오를 만듭니다. 이렇게 하면 시나리오가 원자적이고 이해하기 쉬워집니다.
Given은 본질을 설명해야 하며 구체적인 숫자가 아닙니다. 「Given 사용자 Ivanov, 잔액 500루블」 대신 「Given 잔액이 충분한 사용자」로 작성합니다. 구체적인 데이터는 Examples 테이블이 있는 Scenario Outline으로 이동합니다. 이렇게 하면 시나리오가 보편적이고 재사용 가능해집니다.
Given-When-Then 시나리오를 지속적 통합 파이프라인에 통합하면 문서에서 회귀 방지로 전환됩니다. 모바일 프로젝트의 각 병합 요청은 자동으로 BDD 시나리오를 실행하고 하나 이상의 시나리오가 실패하면 병합을 차단합니다.
Android용 Cucumber의 BDD 시나리오는 Gradle 태스크 ./gradlew cucumber를 통해 실행됩니다. iOS(Quick/Nimble)의 경우 xcodebuild test를 통해 실행됩니다. CI 시스템(GitHub Actions, GitLab CI, Bitrise)에서 BDD 테스트는 에뮬레이터 또는 실제 디바이스에서 실행됩니다. 보고서는 관리자가 이해할 수 있는 HTML 형식으로 생성됩니다: 녹색 시나리오는 통과, 빨간색은 단계를 표시한 실패입니다.
.feature 파일은 코드와 함께 저장소에 저장되며 코드 리뷰를 거칩니다. 분석가는 개발 시작 전에(BDD 우선) 새 시나리오가 포함된 병합 요청을 생성합니다. 개발자는 이러한 시나리오를 통과시키기 위해 단계 정의와 구현을 작성합니다. 모든 시나리오가 통과하면 기능이 완료됩니다. Gojko Adzic의 저서 『Specification by Example』(2011)에 설명된 이 접근 방식은 요구사항을 실행 가능한 결과물로 변환합니다.
자주 묻는 질문
구조적으로는 네, 동일한 세 부분 패턴입니다. 차이는 대상 사용자에 있습니다: Given-When-Then은 비즈니스 언어 중심으로 BDD에서 Gherkin과 함께 사용되는 반면, Arrange-Act-Assert는 단위 테스트를 위한 기술적 형식입니다. 선택은 컨텍스트와 팀에 따라 다릅니다.
제한은 없지만 Then당 3~5개 이하의 검증을 권장합니다. 검증이 더 많으면 시나리오가 하나의 동작에서 너무 많은 것을 확인하고 있을 수 있습니다. 다른 Then을 가진 여러 시나리오로 분할하세요.
아니요. 이 패턴은 모든 테스트 프레임워크에서 사용할 수 있으며, 주석이나 빈 줄로 테스트를 세 블록으로 나누기만 하면 됩니다. Gherkin은 Cucumber 또는 SpecFlow용 .feature 파일 형식으로 시나리오를 작성하는 경우에만 필요합니다.
반복되는 전제조건은 Background(Gherkin) 또는 @Before 메서드(JUnit)로 추출하는 것이 좋습니다. 전제조건이 복잡한 경우 Builder 패턴을 사용하여 테스트 데이터를 만듭니다. 이렇게 하면 Given을 짧고 읽기 쉽게 유지할 수 있습니다.
아니요. When은 동작을 설명하는 필수 블록입니다. 시나리오가 동작 없이 상태만 확인하는 경우(예: 「애플리케이션 로드 시 데이터가 캐시되어야 함」), When은 트리거를 설명합니다: 「애플리케이션이 시작될 때」.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.