Given-When-Then:とは何か、シナリオの構造と例

著者: IT Sectr 公開日: 2026-04-10 読了時間: 8 分

Given-When-Thenは、テストシナリオを記述するための構造パターンであり、BDDがドメイン駆動設計から借用し、Behaviour-Driven Development向けに適応したものです。この形式はシナリオを3つの論理部分(前提条件(Given)、アクション(When)、期待結果(Then))に分割します。Martin Fowler(2023)によると、Given-When-Thenは単なるテストの形式ではなく、実装開始前に要件分析とシナリオ設計を規律づける思考ツールです。

重要なポイント

  • Given-When-Then — 3つのブロック(コンテキスト、アクション、結果)からなるシナリオ記述パターン
  • Givenはテスト対象アクション実行前のシステムの初期状態とデータを設定する
  • Whenはテスト対象ロジックを起動するイベントまたはアクションを記述する
  • Thenは状態の期待される変更または戻り値を検証する
  • Arrange-Act-Assert — ユニットテストにおけるGiven-When-Thenの同等物だが、ビジネス言語指向ではない

Given-When-Thenとは?

Given-When-Thenは、Dan Northが2006年にBehavior-Driven Development手法の一部として初めて策定した動作記述パターンです。このパターンは、ランダムな順序で前提条件、アクション、検証が混在することが多い、構造化されていないテストシナリオ記述の問題を解決します。

このパターンの主なアイデアは、3つのブロック間の責務の分離です。各ブロックはシナリオの1つの側面(前の状態、イベント中、後の検証)のみを担当します。これにより、シナリオが読みやすく、検証可能で、自動化可能になります。Cucumberフレームワークの開発者による研究(2024年)によると、Given-When-Thenパターンに厳密に従ったシナリオは、新しいチームメンバーが理解するのに42%少ない時間で済みます。

パターンの起源

Dan Northは、TDDにおけるテストの策定Test-by-Example手法(Brian Marickが作成)から3部構成のアイデアを借用しました。Marickは、同時にテストとして機能する例(examples)を通じて要件を記述することを提案しました。Given-When-Thenはこのアイデアを形式化し、構造化されていない例を反復可能なパターンに変えました。

適用範囲

Given-When-Thenパターンは、Gherkinを使ったBDDシナリオだけでなく、JUnit、XCTestなどのフレームワークを使った一般的なユニットテストにも適用されます。テストを3つのブロックに分割するコード内のコメントは、テストベースの可読性を向上させる一般的な手法です。Googleはその著書『Software Engineering at Google』(2020年)でこのアプローチを推奨しています。

3つのブロックの構造

Given-When-Thenの各ブロックには、厳密に定義されたセマンティクスと記入ルールがあります。これらのルールに違反すると、自動化や理解が困難なシナリオになります。

Given:前提条件

Givenブロックは、テスト対象アクションを実行する前のシステムの状態を記述します。これには、既存のオブジェクト(ユーザー、注文、設定)、アクティブな状態(認証済み、ネットワーク接続済み)、データの初期値が含まれます。各Givenは検証可能でなければなりません。システムの状態がGivenと一致しない場合、シナリオをスキップするか、テスト環境を事前に準備する必要があります。

When:アクション

Whenブロックは、テスト対象の動作を開始する唯一のイベントを記述します。これはメソッド呼び出し、ボタンクリック、通知の受信、サーバーからの応答などです。重要なルールはシナリオにつき1つのWhenです。アクションのシーケンスを検証する必要がある場合は、Whenの連鎖ではなく、個別のシナリオを作成します。

kotlin
// 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:期待結果

Thenブロックは、システムが期待された状態に移行したことを検証します。これには、戻り値、オブジェクトの状態変更、外部サービス呼び出し(モック検証による)、UI変更が含まれます。各Thenブロックには複数の検証を含めることができますが、すべてが1つのアクションに関連しています。

Given-When-ThenとArrange-Act-Assert

Given-When-ThenArrange-Act-Assert(AAA)は、同じ3部構成パターンの2つのバリエーションですが、対象とするユーザーが異なります。それらの違いを理解することで、特定のタスクに適切な形式を選択するのに役立ちます。

側面Given-When-ThenArrange-Act-Assert
起源BDD、ビジネス分析ユニットテスト
言語自然言語(Gherkin)コード(Kotlin、Swift、Java)
対象チーム全体+クライアント開発者
詳細レベル高レベル詳細
自動化Cucumber、SpecFlowJUnit、XCTest、Mockito

Given-When-Thenを使うべき時

Given-When-Thenパターンは、クライアントやアナリストと議論するシナリオ(機能の受け入れ基準、ユースケース、回帰チェック)に最適です。Gherkin構文を使用すると、プログラミング知識がなくてもこれらのシナリオを記述できます。

Arrange-Act-Assertを使うべき時

Arrange-Act-Assertは、特定のメソッドやクラスを検証するユニットテストの自然な選択肢です。AAA形式は追加のフレームワークを必要とせず、任意のプログラミング言語で機能します。iOS開発では、AppleはXCTestのドキュメント(2024年)でAAAを推奨しています。

Kotlinでのシナリオ例

Androidアプリケーション向けのKotlinでのGiven-When-Thenの実践例を見てみましょう。最初の例はMockKを使用したショッピングカートのテストです。2つ目はプッシュ通知ロジックのテストです。

例1:ショッピングカート

kotlin
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)
    }
}

例2:コルーチンを使用したプッシュ通知

2つ目の例は、非同期コードを使用したGiven-When-Thenを示しています。ここではGivenがFirebase Cloud Messagingの状態を設定し、Whenがプッシュ通知の受信、Thenが処理の検証を行います。

kotlin
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)
    }
}

例3:認証用のGherkinシナリオ

3つ目の例は、受け入れテストのコンテキストでGiven-When-Thenを示すGherkinでのBDDシナリオです:

gherkin
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を効果的に適用するには、いくつかの実証済みのプラクティスに従う必要があります。これらはシナリオの可読性、保守性、自動化を保証します。

シナリオにつき1つのWhen

厳格なルール:1つのシナリオ=1つのアクション。複数のWhenのシーケンスを検証する必要がある場合は、前の結果が次の前提条件となる複数のシナリオを作成します。これにより、シナリオが原子的で理解しやすくなります。

Givenに具体的なデータを避ける

Givenは本質を記述すべきであり、具体的な数字ではありません。「GivenユーザーIvanov、残高500ルーブル」ではなく、「Given十分な残高を持つユーザー」とします。具体的なデータはExamplesテーブルを持つScenario Outlineに移動します。これにより、シナリオが普遍的で再利用可能になります。

  • Thenは測定可能なアサーションとして書く — 「ユーザーはログイン画面を見るべき」であり、「ユーザーはリダイレクトされるべき」ではない
  • 同じタイプのステップにはAndを使用する — 複数のGivenが必要な場合は、Andを使用して結合し、2つ目のGivenを作成しない
  • 抽象度のレベルを混在させない — Given-When-Thenは同じレベル(ビジネスレベルまたは技術レベルのいずれか)であるべきで、混在させてはいけない
  • シナリオの理由を文書化する — ビジネスルールの説明を含む.featureファイルの先頭のコメントがコンテキストに役立つ

CI/CDパイプラインにおけるGiven-When-Then

Given-When-Thenシナリオを継続的インテグレーションパイプラインに統合することで、ドキュメントからリグレッションに対する保護に変わります。モバイルプロジェクトの各マージリクエストは自動的にBDDシナリオを実行し、少なくとも1つのシナリオが失敗した場合、マージをブロックします。

シナリオの自動実行

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はArrange-Act-Assertと同じですか?

構造的にははい、同じ3部構成パターンです。違いは対象ユーザーにあります:Given-When-Thenはビジネス言語指向でBDDでGherkinとともに使用され、Arrange-Act-Assertはユニットテスト用の技術的形式です。選択はコンテキストとチームによって異なります。

Thenブロックにはいくつの検証を含められますか?

制限はありませんが、1つのThenにつき3〜5個以下の検証が推奨されます。検証が多い場合、シナリオが1つのアクションでチェックしすぎている可能性があります。異なるThenを持つ複数のシナリオに分割してください。

Given-When-ThenをGherkinで書く必要はありますか?

いいえ。このパターンは任意のテストフレームワークで使用でき、コメントや空行でテストを3つのブロックに分割するだけです。GherkinはCucumberやSpecFlow用の.featureファイル形式でシナリオを書く場合にのみ必要です。

Givenの長い前提条件はどうすればよいですか?

繰り返しの前提条件はBackground(Gherkin)または@Beforeメソッド(JUnit)に抽出することをお勧めします。前提条件が複雑な場合は、Builderパターンを使用してテストデータを作成します。これにより、Givenを短く読みやすく保てます。

Whenブロックは空でも大丈夫ですか?

いいえ。Whenはアクションを記述する必須ブロックです。シナリオがアクションなしで状態のみを検証する場合(例:「アプリケーション読み込み時にデータがキャッシュされるべき」)、Whenはトリガーを記述します:「アプリケーションが起動するとき」。

まとめ

  • Given-When-Then — シナリオ記述のための3部構成パターン:前提条件、アクション、期待結果
  • Givenはコンテキストと初期状態を設定し、Whenは唯一のアクション、Thenは結果の検証
  • Arrange-Act-AssertとGiven-When-Thenは、対象ユーザーと抽象度が異なる同じパターン
  • このパターンはBDD(Gherkin、Cucumber)および一般的なユニットテスト(JUnit、XCTest)のコメントを通じて適用される
  • 重要なルール:シナリオにつき1つのWhen — 各アクションは個別に検証する必要がある
  • 繰り返しの前提条件は重複を減らすためにBackgroundまたは@Beforeメソッドに抽出される
  • Examplesテーブルを持つScenario Outlineは、コードの重複なしで異なるデータセットでGiven-When-Thenをパラメータ化できる

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください