はじめに
「絶対に面白いから」と10年以上言われ続け、今年の夏にようやく『ONE PIECE』1100話を10日間で完走したbakibakibaです。short-term memoryが貧弱なため、既に初期キャラがあまり思い出せません。僕はついていけるだろうか、今後のワンピの展開に...。
さて、昨今AIコーディングが巷を賑わせていますね。
例にもれず自分もchatGPTにコードを書かせてみたり、VSCodeにClineを入れてみたりしたのですが実際に書いてくれたコードを脳死でnpm run devして動かしてみると、ふと思うことがあります。
なんか絶妙に動かなくね?これ
一発でそれっぽいコードは出てくるのですが、最終的には人の手で少し修正を入れることがしばしば発生します。この記事ではCucumberとPlaywrightを使ってAIコーディングをBDDで行い、成果物の動作を担保しようとする試みを紹介します。
BDDとは
BDD(Behaviour-Driven Development、振る舞い駆動開発) とは、開発対象のソフトウェアの振る舞いを自然言語で記述し、それを正として開発を行う手法です。
BDDを実現するにあたり、今回は以下のフローに沿って開発&テストを行いました。
- ソフトウェアの振る舞いの記述
Gherkin記法で振る舞いを記述します。 - 挙動に対する具体的なテストの記述
E2EテストツールであるPlaywrightでテストを記述します。 - テストの実行
テスト自動化ツールであるCucumberを使って、ソフトウェアが上記で定義したふるまいに沿って動作するかテストします。
Gherkin記法とは
Gherkin記法はシナリオ記述フォーマットの一つです。今回利用するCucumberがサポートしています。
以下のように、シナリオに対するGiven/When/Thenを記述します。
以下の例は記述言語に日本語を指定しているので、Given/When/Thenは各々
前提/もし/ならばに対応します。
https://cucumber.io/docs/gherkin/languages/
# language: ja
機能: シンプルTODOの管理
ライトユーザーとして、
タスクを追加・完了・削除したい。
それによって、日常の小さなタスクをすぐに記録し管理できる。
背景:
前提 私は認証不要のシンプルなTODOリストを利用している
シナリオ: 新しいタスクを追加すると一覧に表示される
前提 私はタスクが0件のTODOリストを見ている
もし 私が「牛乳を買う」というタスクを追加する
ならば タスク一覧に「牛乳を買う」が表示される
Playwrightとは
PlaywrightはMicrosoftが開発したE2Eテストツールです。
- 全ブラウザ対応: Chromium・Firefox・WebKit(Safari)の全てで動作
- とにかく速い: 強力な並列実行により、テスト待ち時間のストレス減
- 失敗原因が一目瞭然: スクリーンショットや操作動画を自動で記録
技術的な詳細は割愛しますが、個人的にはDXが良いことも気に入っています。
VSCode拡張も非常に使いやすいのですが、最近ではMCPサーバーへの対応も進んでおり、AIツールと組み合わせてブラウザ操作を自動化するなどテストの枠を超えた可能性を秘めています。
以下の記事で紹介されているので、ぜひ読んでみてください。
Cucumberとは
CucumberとはBDDのサポートツールです。
Cucumberを利用すると、Gherkin記法で記述したシナリオに沿ってテストを実行することができます。今回は、そのテスト部分をPlaywrightで記述しています。
実際にコード例を見てみましょう。
以下はGherkin記法の例として記載したTODOアプリのテストコードです。
import {
Given,
Then,
When,
// その他色々importしてるが省略
} from "@cucumber/cucumber";
import {
expect,
// その他色々importしてるが省略
} from "@playwright/test";
// CustomWorldやseedTodosなどの定義は省略
Given("私はタスクが0件のTODOリストを見ている", async function (this: CustomWorld) {
await seedTodos(this.page, this.baseURL, []);
});
When("私が「牛乳を買う」というタスクを追加する", async function (this: CustomWorld) {
await this.page.goto(this.baseURL + "/");
await this.page.getByLabel("新しいタスク").fill("牛乳を買う");
await this.page.getByRole("button", { name: "追加" }).click();
});
Then("タスク一覧に「牛乳を買う」が表示される", async function (this: CustomWorld) {
await expect(this.page.getByRole("list")).toContainText("牛乳を買う");
});
勘のいい人は気づいたかもしれないですが、Gherkin記法で書いたシナリオの「前提/もし/ならば」の次に書いた文章が、そのままCucumberライブラリが提供している「Given/When/Then」関数の引数になっています。これによってCucumberはシナリオとテストコードを紐づけてテストを実行してくれます。
どうやって実行するの?
Javascriptでテスト書きたい場合
以下の公式ドキュメントに沿って構築すれば、JavascriptでCucumberを実行することができます。
Typescriptでテストを書きたい場合
Typescriptでテストを実行したい場合は、Javascriptの対応に追加して以下を対応する必要があります。
-
ts-nodeのインストールnpm i ts-node -
cucumber.jsの修正cucumber.jsmodule.exports = { default: { requireModule: ["ts-node/register"], // ここを追記 require: ["features/step_definitions/**/*.ts"], // ここも追記 }, };
最終的には以下のようなディレクトリ構成になると思います。
.
├── cucumber.js //
├── features
│ ├── todo.feature // Gherkin記法によるシナリオ
│ └── step_definitions
│ └── todo_steps.ts/js // シナリオに紐づくテストコード
└── package.json
npm run testで実行することができます。
$ npm run test:e2e
> simple-todo-app@0.1.0 test:e2e
> cucumber-js
5 scenarios (5 passed)
20 steps (20 passed)
0m08.153s (executing steps: 0m07.852s)
実際にAIにやらせるときはどうするの?
Clineを使って開発した際は、以下のようにプロンプトで指示していました。
1. ユーザーからの要求を、対話を通じて要件定義書(REQUIREMENTS.md)にまとめる。
2. REQUIREMENTS.mdを解釈し、最新のベストプラクティスに基づいたNext.js環境を構築する。
3. REQUIREMENTS.mdからアプリの仕様を作成し.featureファイルにまとめる。
4. .featureファイルを基にPlaywrightを使用したテストを<feature>_steps.tsファイルに書く。
5. package.json・.featureファイル・<feature>_steps.tsファイルからコードを書く
6. テストが通るか確認する。通らなければ、コードを修正する。
一旦適当に投げた要件を精緻化してもらい、それをベースにBDDさせ最低限の動作確認をさせています。BDDさせることで、「なんやこれ動かんやん」から「あーまぁ動くけど~」くらいに自分は持っていけました。
Playwrightで動作確認の証左として、スクショも撮れるのも嬉しいですね。

問題点
そもそもアプリの要件が適当なので、それほどのクオリティは求めていなかったのですが、何度もやっていて以下のような問題点があることを認識しています。
- テストが間違っており、コードの修正が終わらない。
無限トークン編になる前に、テスト自体を見直してもらうように指示したい。 - スクラッチ開発は行けるものの、エンハンスの適用性は低い。
要件やコードが複雑になってくるとテストが通りにくくなってくる。
今はハッカソンとかPoCで簡単に使う分に留めておきたいです。
参考文献