Cucumber・JUnit・Seleniumを駆使してブラウザテストを自動化する

# 概要

Cucumber・JUnit・Seleniumを組み合わせて、  
ブラウザテストを自動化するための実装について紹介します。

デモ用の[GitHubリポジトリ](https://github.com/KentoHummer/cucumber-junit-selenium-demo)も作成しているので、参考にどうぞ！

# ライブラリ

## Cucumber関連

言わずと知れたBDDフレームワークですね。  
本記事では以下のライブラリを使用します。

- [io.cucumber:cucumber-java](https://mvnrepository.com/artifact/io.cucumber/cucumber-java)
    - CucumberをJavaで使用するために必要です
- [io.cucumber:cucumber-junit-platform-engine](https://mvnrepository.com/artifact/io.cucumber/cucumber-junit-platform-engine)
    - CucumberとJUnitを紐づけるために必要です

## JUnit関連

Javaでテストと言えばド定番（というかこれ以外に選択肢が無い...笑）のテスティングフレームワークですね。  
本記事では以下のライブラリを使用します。

- [org.junit.jupiter:junit-jupiter-api](https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api)
    - JUnitのコアとなるライブラリです。本記事ではJUnit5を使用します
- [org.junit.jupiter:junit-jupiter-engine](https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine)
    - JUnitのテスト実行時に必要です
- [org.junit.platform:junit-platform-suite-api](https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite-api)
    - テストランナー実行時に必要です
- [org.junit.platform:junit-platform-suite-engine](https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite-engine)
    - テストランナー実行時に必要です

## Selenium関連

ブラウザ操作自動化の定番フレームワークですね。  
本記事では以下のライブラリを使用します。

- [org.seleniumhq.selenium:selenium-java](https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java)
    - SeleniumをJavaで使用するために必要です
- [io.github.bonigarcia:webdrivermanager](https://mvnrepository.com/artifact/io.github.bonigarcia/webdrivermanager)
    - ブラウザ毎に必要となるドライバーを管理してくれる便利なライブラリです

# ビルド構成

ビルド構成のサンプルとしてbuild.gradleを貼っておきます。

```build.gradle
plugins {
    id 'java'
}

tasks.withType(JavaCompile).configureEach {
    options.encoding = 'UTF-8'
}

group 'kentohummer.cjsdemo'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

// デモ用のためライブラリのバージョンについては最新リリース指定としています（本来は固定のバージョン指定の方が望ましいです）
dependencies {
    // Cucumber関連のライブラリ
    testImplementation 'io.cucumber:cucumber-java:latest.release'
    testImplementation 'io.cucumber:cucumber-junit-platform-engine:latest.release'
    // JUnit関連のライブラリ
    testImplementation 'org.junit.jupiter:junit-jupiter-api:latest.release'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:latest.release'
    testImplementation 'org.junit.platform:junit-platform-suite-api:latest.release'
    testRuntimeOnly 'org.junit.platform:junit-platform-suite-engine:latest.release'
    // Selenium関連のライブラリ
    testImplementation 'org.seleniumhq.selenium:selenium-java:latest.release'
    testImplementation 'io.github.bonigarcia:webdrivermanager:latest.release'
}

test {
    useJUnitPlatform()
}
```

# 実装

## Cucumberのfeatureファイルを作成してテストシナリオを記述

```sample.feature
# フィーチャーのサンプルファイルです
Feature: サンプルのフィーチャー1

    Scenario: シナリオ1
        Given 前提条件1
        When アクション1をした時
        Then 結果1になる
```

- シナリオの書き方はGherkin記法で記載します
    - [公式リファレンス](https://cucumber.io/docs/gherkin/reference/)
- ここに画面操作などの各ステップを記述していくことになります

## ステップ定義クラスを作成してステップの実装を行う

```SampleStep.java
package kentohummer.cjsdemo;

import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.chrome.ChromeDriver;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * ステップ定義のサンプルクラスです.
 */
public class SampleStep {

    private static ChromeDriver chromeDriver;

    @Given("^前提条件1$")
    public void prerequisites1() {
        // Chromeのドライバを起動
        WebDriverManager.chromedriver().setup();
        chromeDriver = new ChromeDriver();
    }

    @When("^アクション1をした時$")
    public void action1() {
        // Cucumberの公式リファレンスを開く
        chromeDriver.get("https://cucumber.io/docs/cucumber/api/?lang=java");
    }

    @Then(value = "^結果1になる$")
    public void result1() {
        // 正しく遷移できているか検証
        assertEquals(chromeDriver.getCurrentUrl(), "https://cucumber.io/docs/cucumber/api/?lang=java");

        // Chromeのドライバを停止
        chromeDriver.quit();
    }
}
```

- 前述のfeatureファイルの各ステップ（Give、When、Thenの部分）に対応する形でJavaでテストロジックを記載していきます
- Seleniumを駆使する場合、上記サンプルのように各ステップでブラウザドライバを使用して画面操作の処理を実装します
- 今回はブラウザアクセスのテストのみですが、実際にはここに各種画面操作のテストケースを実装していくことになります

## テストランナークラスを作成してテストの実行構成を定義

```SampleRunner.java
package kentohummer.cjsdemo;

import org.junit.platform.suite.api.*;

import static io.cucumber.core.options.Constants.GLUE_PROPERTY_NAME;
import static io.cucumber.core.options.Constants.PLUGIN_PROPERTY_NAME;

/**
 * ランナーのサンプルクラスです.
 */
@Suite
@SelectClasspathResources({
        // 実行したいfeatureファイルのパスを指定
        @SelectClasspathResource("sample.feature")
})
@ConfigurationParameters({
        // ステップ定義クラス群のパッケージを指定
        @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "kentohummer.cjsdemo"),
        // レポートの出力形式や場所を指定
        @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
})
public class SampleRunner {
}
```

- @SelectClasspathResourceアノテーションを使用して、featureファイルのパスを指定します
- @ConfigurationParameterアノテーションを使用して、ステップ定義クラスの紐付きや、レポートの出力形式などを指定します
- アノテーションの記述だけをしておけば、後は各種ライブラリが解釈してくれるので、このクラス自体は実装を持たなくても良いです

# テストを実行する

実装したランナークラス（SampleRunner）を実行すれば、実際にブラウザテストが自動実行されます。  
このサンプル実装の場合、  
Chromeを立ち上げてCucumberの公式リファレンスページに飛び、その後、実際に飛べているかどうかを検証する  
という一連のテストが実行されます。

# おわりに

Cucumber・JUnit・Seleniumを組み合わせると、テストを自動化できるのは勿論のこと、  
CucumberがBDDに基づいているため、非エンジニアも積極的にテストケースの作成などに参加できるのがGoodですね。  
featureファイルについては非エンジニアに作成してもらって、  
実際のテストロジック部分はエンジニアが実装するといったことも容易にできると思います。

また、CI/CDにも容易に組み込めるため、より品質の高いプロダクト開発が行えそうですね！

何より、テストを自動化しておくことで、  
面倒なブラウザテスト（よくある結合テストフェーズでの、エビデンス貼り貼り作業）を自動で行えるのが熱いですね...！