BDDを導入してみて実際どうだったか
はじめに
こんにちは、ログラスの村本です。
最近、チームのプロダクト品質について改善する機会があり、その文脈でBDDを導入したので、その時のお話を書こうと思います。
この記事では、BDDの導入の背景から、どのように導入して、実際どうだったかまでを紹介しようとおもいます。
同じような課題を抱えるチームの皆さんにとって、この記事が何かしらのヒントになれば幸いです☺️
BDDを導入した目的
当時のチームが抱えていた課題
ログラスもリリースから4年以上が経ち、プロダクトが成長し、これまで対応できなかったユースケースに対応できるような機能が増えてきました。
その一方で、相対的に仕様が複雑化しつつあり、特定の変更を加えたときの影響範囲の考慮漏れなどが起こるようになってきました。
ログラスにはプロダクトの初期段階からテストコードを書く文化があり、ドメインモデルに対するUnitTestや、UseCaseクラスのIntegrationTestはかなり充実していました。
また、開発前にテスト分析設計を実施し、デシジョンテーブルを用いて網羅的にパターンを洗い出すなども行っていました。
それでも、影響範囲の考慮漏れは起こっていました。
これは単体のUseCaseレベルのテストはできていたが、UseCase外の状態やコンテキストを含めたシナリオレベルのテストが不足していると考えました。
シナリオレベルのテストでいうと、ログラスにはDataDog Syntheticsを使ったE2Eテストも存在していましたが、一部の主要な機能を薄く一通り撫でるシナリオが存在するだけでした。
ここに改善の余地があると感じ、E2Eの拡充を最初は検討し始めましたが、E2Eテストはメンテナンスコストが高く、安定性も低いため、必要なところに絞って書くようにしたいと思ったのですが、「そもそも、どんなテストがE2Eに必要なのか?」という疑問を持ち、その過程で出会ったのがBDD (Behavior Driven Development)というアプローチでした。
BDDとは?
BDDは振る舞い駆動開発というソフトウェア開発手法で、ソフトウェアの仕様や振る舞いを、開発者・QA・PdMなど関係者全員が共通の言語で記述し、対話を通じて明確化していく開発手法です。
BDDと聞くと Given/When/Then
で振る舞いを意識してテストを書くものというイメージがありますが、BDDは開発手法であり、テストに閉じず、開発プロセス全体に関わります。
(BDDの歴史的な背景的に、ただテストを先に書くだけのTDDに対するものとして提唱されたものらしいので、自分も Given/When/Then
でテストを書くものというイメージがありました。)
https://cucumber.io/docs/bdd/#three-practices
BDDでは、以下の3つのステップを繰り返しながら開発を進めます。
- Discovery (発見): 開発者・QA・PdMなど関係者全員でユースケースを取り上げ、具体的な「例」を出し合い、仕様や要求の認識を揃える
- Formulation (定式化): 発見した「例」を自動化できる方法 (Gherkin記法など) で誰でも読める形に落とし込む
- Automation (自動化): 定式化したシナリオを自動テストとして実装し、CI/CDで常に検証できるようにする
BDDについてはCucumberのドキュメント や 風間さんの記事 が大変わかりやすいので、そちらに目を通していただけると良いかもです。
(記事の最後にBDDをキャッチアップする上での参考文献も乗せておきます)
BDDに期待したこと
BDDのサイクルを通して、E2Eで 「検証すべきシステムの振る舞いが何か」 を明確にするというところが最も期待したポイントでした。
各サイクルでの効果として期待していたのは次のようなものです。
-
仕様策定の初期段階から関係者全員で対話し、認識のズレや考慮漏れを減らすこと
- BDDのDiscovery (発見)のステップで、開発者・QA・PdMなど多様な視点から具体的な例を出し合い、仕様の抜けや曖昧さを早期に発見できることを期待しました。
-
「何を検証すべきか」の共通理解を深めること
- シナリオを通じて、チーム全体で「本当に必要な振る舞い」を合意形成し、実装やテストの指針を明確にできると考えました。
-
生きた仕様書としてのシナリオの維持
- Gherkin記法で記述したシナリオが、そのまま自動テストとして機能し、CI/CDで常に検証されることで、実装とドキュメントの乖離を防げることを期待しました。
導入してみて、どうだったかは後述します。
BDDをどのように導入したか
ここからは、私たちのチームがどのようにBDDを導入していったのかをご紹介します。
Discovery (発見):シナリオ作成前の対話と認識合わせ
本格的なシナリオ作成の前に、Three Amigos (PdM、開発者、QA)で集まり、実例マッピングやイベントストーミングを行い、具体的な例を出し合いながら、ビジネスルールを発見します。
Formulation (定式化):シナリオの記述ルール
仕様書となる.feature
ファイルは、Gherkin記法で記述します。
誰が読んでも理解でき、かつメンテナンスしやすい状態を保つため、BRIEFの原則に基づいたいくつかのルールを設けました。
BRIEFの原則については、以下の記事が大変わかりやすいです。
設けたルールをそれぞれ説明していきます。
1. ビジネス価値にフォーカスする
ユーザーにとって価値のある振る舞いのみをシナリオとして記述するようにしています。
ビジネス価値がないシナリオも書いてしまうと、保守対象を増やすだけでなく、重要なシナリオを隠してしまうためです。
具体的なシナリオの例を見てみましょう。
NG: ユーザー価値に直結しないシナリオの例
Scenario: 単価に数字以外を入力できない
Given ユーザーが商品登録画面を開いている
When 単価欄に「abc」と入力する
Then 「単価は数字で入力してください」というエラーメッセージが表示される
この例は「入力バリデーション」という実装上の制約にフォーカスしており、ユーザーが本当に達成したい目的や価値が伝わりません。
また、単なる入力バリデーションのテストであれば、UnitTestやIntegrationTestでも十分担保できると思います。
OK: ユーザー価値に直結するシナリオの例
Scenario: 売上高を正しく計算できる
Given 商品単価が100円に設定されている
And 2025年6月の販売数量が5個に設定されている
When 2025年6月の売上高を計算する
Then 2025年6月の売上高が500円と表示されること
この例はGivenで商品単価と販売数量の設定という別々のUseCaseを組み合わせて、売上高を計算するための準備をしています。
「売上計算」というユーザーの目的・価値にフォーカスしており、機能の重要な振る舞いが自然言語で確認できるようになっています。
2. シナリオは単独で実行可能にする
シナリオは単独で実行可能な状態にする必要があります。
他のシナリオで変更された状態に依存するシナリオは絶対に書いてはなりません。
仮に他シナリオに影響を与える変更を加えた場合には、cleanup処理を行うようにします。
他のシナリオに依存したシナリオが存在すると、並列実行時にテストが失敗する可能性があります。
NG:2つのシナリオが依存している
Scenario: 商品を登録する
Given 管理者が商品登録画面を開いている
When 商品「りんご」を登録する
Then 商品一覧に「りんご」が表示されていること
Scenario: 商品の在庫を減らす
When 商品「りんご」を1個購入する
Then 商品「りんご」の在庫が1減っていること
この例では、「商品の在庫を減らす」シナリオが「商品を登録する」シナリオで登録された「りんご」の存在に依存しています。
シナリオを単独で実行した場合や並列実行した場合、前提が満たされずテストが失敗する原因となります。
OK:前提をすべてGivenで明示している
Scenario: 商品の在庫を減らす
Given 商品「りんご」が在庫5個で登録されている
When ユーザーが「りんご」を1個購入する
Then 商品「りんご」の在庫が4個になっていること
この例では、必要な前提条件 (商品名・在庫数)をすべてGivenで明示しているため、他のシナリオに依存せず、単独で何度でも安定して実行できます。
3. 5行以内に抑える (簡潔さ)
シナリオには原則的に1つのビジネスルールのみを含めるようにしてください。
ついついシナリオを長くしたくなりますが、長いシナリオには複数のビジネスルールが含まれている可能性が高いです。
わかりやすい基準として、シナリオは簡潔に5行以内に抑えるようにしましょう。
BRIEFでいうと、焦点を絞る (Focused) と簡潔にする (Brief)に当たる内容です。
4. 宣言的に記述する
シナリオのステップは、宣言的に記述することで、シナリオの意図が明確になります。
良くない例は、命令的に操作のシーケンスを記述することです。
宣言的と命令的な違いは英語で記述すると違いが分かりやすいです。
BRIEFでいうと、意図を明らかにする (Intention revealing)に当たる内容です。
NG: 命令的な例
UIの操作をシーケンスに書いてしまっている例です。
振る舞いに変更がなくても、UIの変更があるとテストが壊れてしまうため、メンテナンスが困難になります。
Scenario: 会計システムから財務実績をアップロードする
Given 経理としてログイン // login as an accountant
And 実績アップロードページにアクセス // access upload page
And アップロードボタンをクリック // click upload button
And 会計システムを選択 // select accounting system
And ファイルを選択 // select file
When アップロードボタンをクリック // click upload button
Then 財務実績ページにアップロードされた履歴が追加されること // upload history is added to financial performance page
OK: 宣言的な例
振る舞いを宣言的に記述している例です。
英文で見るとわかりやすいですが、誰が何をするのかが宣言的に記述されています。
こうすることでUIに変更があった場合も、シナリオに手を加える必要はなく、裏側のテスト実装を変更するだけで良くなります。
Scenario: 会計システムから財務実績をアップロードする
Given 経理としてログインしている // He is logged in as an accountant.
When 会計システムから財務実績をアップロードする // He uploads financial performance from accounting system.
Then 財務実績ページでアップロード履歴が確認できる // He should be able to see the upload history on financial performance page.
Automation (自動化):シナリオの実装と運用
Gherkin記法のシナリオは裏側でテストコードと接続することができます。
テストを安定化&高速化させるために、以下の実装方針を定めました。
- Given : テストの保守性を上げるために、API経由でテスト可能な状態を作るようにしましょう。
- When : 基本的にはUIを操作して、必要なデータが表示されユーザーが操作するようにしましょう。
- Then : 基本的にはユーザーが確認可能な振る舞いを検証するようにしましょう。UI上での確認が基本ですが、API経由での確認で十分な場合はそちらを利用しましょう。
When
では、Given
で準備されたデータが存在し、ユーザーが操作可能な状態であることを検証したいため、UI上から操作するように明記しています。
技術選定
最後に書いたシナリオを自動テストするための技術選定のお話です。
最終的にチームは Cucumber.js + Playwright という組み合わせを選択しましたが、ここに至るまでにいくつかの検討したものがあったので、そちらをご紹介しようと思います。
-
Playwright
- まず、ブラウザ操作のフレームワークとして、強力な機能を持つ Playwright を利用することを前提としていました。
-
playwright-bdd
- 次に、Playwrightとの連携に特化した playwright-bdd を検討しました。しかし、IntelliJのプラグインが存在せず判断し断念しました。
- 改めて見てみると、AIネイティブのサポートもあり、playwright-bddの採用は再検討しても良いかもしれません。
- Playwright公式のBDDサポートを待つ選択肢もありましたし、切望されているIssueもありましたが、現時点ではまだ実装されていないため断念しました。
- 次に、Playwrightとの連携に特化した playwright-bdd を検討しました。しかし、IntelliJのプラグインが存在せず判断し断念しました。
-
Gauge
- 次に Gauge を検討しましたが、調査段階でコアチームがサポートの頻度を減らすと書いてあったため、長期的な運用は難しいと判断し採用を見送りました。
-
Cucumber
- Gherkin記法を扱えるデファクトスタンダードなツールであり、Playwrightと柔軟に組み合わせられる Cucumber を採用することに決定しました。
実際にBDDを導入してみて
ここでは、「BDDに期待したこと」で挙げたポイントごとに、実際に導入してみて得られた効果や気づき、課題についてまとめます。
1. 仕様策定の初期段階から関係者全員で対話し、認識のズレや考慮漏れを減らすこと
実例マッピングを行う場面は増えてきているのを感じています。
実際に実例マッピングをやってみると、どこまでビジネスルールとして扱うのか?という境界の判断が難しいように感じました。
一方で、有識者を交えてユーザーのどの課題を解きたいのかを考えることで、エンジニアがよりユーザーの課題をより深く理解しやすくなったように感じています。
2. 「何を検証すべきか」の共通理解を深めること
「何を作るべきかの共通認識」は実例マッピングを通して深まり、プロセスの中で「ビジネス価値について」考えるため、本当に検証すべきものを考えやすくなったと思います。
BDDでもっとも期待していた 「検証すべきシステムの振る舞いが何か」 という点についてはBDDはかなり効果的だったと感じています。
3. 生きた仕様書としてのシナリオの維持
シナリオがCI/CDで常に検証されることで、シナリオが検証される状態は作ることができましたが、まだ記述されているシナリオ数が少なすぎて、効果が目に見えるほどではありません。
また、仕様という意味ではログラスではサポートサイトがかなり充実しており、リリースに合わせて更新されているので、現状網羅的な仕様としてはサポートサイトの方が充実していると思います。
ここについては期待する効果は現状あまり得られていません。
おわりに
今回は、私たちのチームがBDDを導入した背景から、具体的な運用方法、実際に導入してどうだったかまでをご紹介しました。
BDDはシステムの振る舞いを中心にチームの共通認識を作り、 「何を作るべきか」 についての対話を促進する強力なプラクティスだと感じています。
また、ここ半年でAIもさらに進化しており、AI活用を含めると改善の余地はまだまだあると思います。
例えば、冒頭の課題であった影響範囲の考慮漏れの対策という点では、AIにコードを全てコンテキストとして与えて、「このような変更を加えたときにはどこに影響がでるか調査して」と指示するほうが直接的に効果がありそうです。
この記事が、皆さんのプロダクト開発やテストコード執筆に少しでも役立てていただければ幸いです。
ログラスに少しでも興味を持っていただけた方はPittaでお話できますので、ぜひお話しましょう!
参考文献
-
The BDD Books - Discovery
- 体系的にBDDを学べるとても良い本です。BDDはテストじゃなくて開発手法だということがよく分かる内容になっています。
-
The BDD Books - Formulation
- こちらにはシナリオを記述する際の実践的なノウハウが書かれています。
-
The BDD Books - Automation
- ただいま執筆中なので完成を待っています
-
BDD in Action, Second Edition
- 比較的最近に第2版が出たものになります。英語にはなりますが、体系的な内容から実践的な内容まで包括的に含まれています
Discussion