Gradle セットアップ
// build.gradle
dependencies {
// Cucumber JVM 本体
testImplementation "io.cucumber:cucumber-java:7.17.0"
testImplementation "io.cucumber:cucumber-junit-platform-engine:7.17.0"
// JUnit 5 プラットフォーム
testImplementation "org.junit.platform:junit-platform-suite:1.10.2"
testImplementation "org.junit.jupiter:junit-jupiter:5.10.2"
// Spring Boot 連携(Spring Boot プロジェクトの場合)
testImplementation "io.cucumber:cucumber-spring:7.17.0"
}
// src/test/resources/cucumber.properties
cucumber.publish.quiet=true
cucumber.plugin=pretty, html:build/cucumber-reports/report.html, json:build/cucumber-reports/report.json
// src/test/java/com/example/RunCucumberTest.java
// JUnit 5 Suite で Cucumber を起動するランナークラス
import org.junit.platform.suite.api.*;
@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features") // Feature ファイルの場所
@ConfigurationParameter(
key = "cucumber.glue",
value = "com.example.steps" // Step 定義クラスのパッケージ
)
public class RunCucumberTest { }
Gherkin 文法の基本
Gherkin は自然言語(日本語も対応)で受け入れ条件を記述するための DSL です。
# src/test/resources/features/user-registration.feature
# language: ja
機能: ユーザー登録
利用者として
アカウントを作成することで
サービスを利用できるようになりたい
シナリオ: 正常なユーザー登録
前提 ユーザー登録ページを開いている
もし ユーザー名に "山田太郎" を入力する
かつ メールアドレスに "taro@example.com" を入力する
かつ パスワードに "Password123!" を入力する
かつ "登録" ボタンをクリックする
ならば 登録完了メッセージが表示される
かつ メールアドレス "taro@example.com" に確認メールが送信される
シナリオ: すでに登録済みのメールアドレスはエラー
前提 "taro@example.com" がすでに登録されている
もし ユーザー登録ページを開いている
かつ メールアドレスに "taro@example.com" を入力する
かつ "登録" ボタンをクリックする
ならば "このメールアドレスはすでに使用されています" というエラーが表示される
ℹ️ 英語記法が標準
日本語 Gherkin(# language: ja)も使えますが、チームの英語力に自信があれば英語で書く方が CI 連携・ツールとの相性が良いです。Given / When / Then / And / But が標準キーワードです。
Step 定義の実装
Feature ファイルの各 Given/When/Then に対応する Java の Step 定義を実装します。
// src/test/java/com/example/steps/UserRegistrationSteps.java
import io.cucumber.java.*;
import io.cucumber.java.ja.*; // 日本語アノテーション
import static org.assertj.core.api.Assertions.*;
public class UserRegistrationSteps {
private final RegistrationPage registrationPage;
private final EmailService emailService;
private final UserRepository userRepository;
// Spring DI(@CucumberContextConfiguration が必要)
public UserRegistrationSteps(
RegistrationPage page,
EmailService emailSvc,
UserRepository userRepo) {
this.registrationPage = page;
this.emailService = emailSvc;
this.userRepository = userRepo;
}
@前提("ユーザー登録ページを開いている")
public void openRegistrationPage() {
registrationPage.open();
}
@もし("ユーザー名に {string} を入力する")
public void enterUsername(String name) {
registrationPage.enterName(name);
}
@もし("メールアドレスに {string} を入力する")
public void enterEmail(String email) {
registrationPage.enterEmail(email);
}
@もし("パスワードに {string} を入力する")
public void enterPassword(String password) {
registrationPage.enterPassword(password);
}
@もし("{string} ボタンをクリックする")
public void clickButton(String buttonName) {
registrationPage.clickButton(buttonName);
}
@ならば("登録完了メッセージが表示される")
public void verifySuccessMessage() {
assertThat(registrationPage.getSuccessMessage())
.isEqualTo("ご登録ありがとうございます!");
}
@ならば("メールアドレス {string} に確認メールが送信される")
public void verifyConfirmationEmail(String email) {
assertThat(emailService.getSentEmails())
.anyMatch(e -> e.getTo().equals(email)
&& e.getSubject().contains("確認"));
}
@前提("{string} がすでに登録されている")
public void existingUser(String email) {
userRepository.save(new User("既存ユーザー", email, "Existing123!"));
}
@ならば("{string} というエラーが表示される")
public void verifyErrorMessage(String expectedError) {
assertThat(registrationPage.getErrorMessage()).isEqualTo(expectedError);
}
}
Scenario Outline とパラメータ化
同じシナリオを複数の入力値で繰り返すには Scenario Outline + Examples を使います。
# パスワードバリデーションのパラメータ化テスト
シナリオアウトライン: パスワードバリデーション
もし パスワードに "<password>" を入力して登録する
ならば バリデーション結果は "<result>" になる
例:
| password | result |
| Abc1234! | 成功 |
| abc1234! | 失敗 | # 大文字なし
| Abcdefg! | 失敗 | # 数字なし
| Abc1234 | 失敗 | # 記号なし
| Ab1! | 失敗 | # 7文字未満
| Abc12345678! | 成功 | # 長いパスワード(境界値)
DataTable の活用
複数の属性を持つデータを Step に渡す場合は DataTable を使います。
# Feature ファイル
シナリオ: 複数ユーザーを一括登録する
もし 以下のユーザーを登録する:
| 名前 | メール | ロール |
| 山田太郎 | taro@example.com | ADMIN |
| 鈴木花子 | hanako@example.com | USER |
| 田中一郎 | ichiro@example.com | USER |
ならば ユーザー数が 3 人になる
// Step 定義 — DataTable を受け取る
import io.cucumber.datatable.DataTable;
import java.util.*;
@もし("以下のユーザーを登録する:")
public void registerMultipleUsers(DataTable dataTable) {
// ヘッダー行を除いたデータ行を Map のリストとして取得
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
for (Map<String, String> row : rows) {
String name = row.get("名前");
String email = row.get("メール");
String role = row.get("ロール");
userService.register(name, email, UserRole.valueOf(role));
}
}
@ならば("ユーザー数が {int} 人になる")
public void verifyUserCount(int expectedCount) {
assertThat(userRepository.count()).isEqualTo(expectedCount);
}
Hooks(@Before / @After)
// src/test/java/com/example/steps/Hooks.java
import io.cucumber.java.*;
public class Hooks {
private final DatabaseCleaner dbCleaner;
private final TestContext testContext;
public Hooks(DatabaseCleaner dbCleaner, TestContext testContext) {
this.dbCleaner = dbCleaner;
this.testContext = testContext;
}
// 各シナリオ前にDBをクリーン
@Before
public void beforeEachScenario(Scenario scenario) {
dbCleaner.cleanAll();
System.out.println("開始: " + scenario.getName());
}
// 各シナリオ後にリソース解放・スクリーンショット取得
@After
public void afterEachScenario(Scenario scenario) {
if (scenario.isFailed()) {
// 失敗時にスクリーンショットを埋め込む
byte[] screenshot = testContext.takeScreenshot();
scenario.attach(screenshot, "image/png", "失敗時スクリーンショット");
}
testContext.reset();
}
// 特定タグのシナリオのみに適用
@Before("@api")
public void beforeApiScenario() {
testContext.setApiMode(true);
}
}
タグ(@Tag)と実行フィルタ
# タグ付きシナリオ @smoke @critical シナリオ: ログイン機能の基本動作確認 ... @api @regression シナリオ: ユーザー検索 API の検証 ... @slow @performance シナリオ: 大量データ取込みの性能確認 ... # タグで絞り込んで実行(Gradle) ./gradlew test -Dcucumber.filter.tags="@smoke" # smoke のみ ./gradlew test -Dcucumber.filter.tags="@smoke and @api" # 両タグ ./gradlew test -Dcucumber.filter.tags="not @slow" # slow を除外 # CI での推奨設定(PRでは smoke のみ、mainでは regression 全実行) # .github/workflows/test.yml - name: Run smoke tests (PR) if: github.event_name == 'pull_request' run: ./gradlew test -Dcucumber.filter.tags="@smoke" - name: Run regression tests (main) if: github.ref == 'refs/heads/main' run: ./gradlew test -Dcucumber.filter.tags="@regression"
Feature ファイルの書き方ベストプラクティス
⚠️ 実装の詳細を Feature ファイルに書かない
「パスワードフィールドの CSS クラス '.pwd-input' に入力する」のような UI の詳細は Feature ファイルに書くべきではありません。ビジネス上の振る舞い(「パスワードを入力する」)を記述し、実装の詳細は Step 定義に隠蔽してください。
✅ 1シナリオ = 1ユーザーストーリー
Feature ファイルはビジネスの「受け入れ条件」に直接対応させます。1つのシナリオが長くなりすぎる場合は、機能を適切に分割するシグナルです。シナリオは 5〜8 ステップ以内に収めることを目安にしてください。
まとめ
- Gherkin の Given-When-Then で「前提 / 操作 / 結果」を明確に分離し、非エンジニアも読める仕様書を書く
Scenario Outline + Examplesで境界値・同値クラスのパラメータ化テストを簡潔に表現するDataTableで複数データをステップに渡し、テストデータをFeatureファイルに集約する@Before / @Afterで各シナリオの初期化・後片付けを一元管理し、テストの独立性を確保する- タグで CI のテスト実行を制御し、PR ではスモークテスト、main では全回帰テストを実行する