Selenide+Selenoid+GaugeでE2Eテストを自動化する【テストコード編】

## はじめに
Selenide、Selenoid、Gaugeを利用したE2Eテストのテストコードについて紹介します。
本サンプルは、Googleでselenideまたはselenoidを検索し、検索結果から公式ページのリンクを辿り、公式ページが開くことを検証する簡単なサンプルです。

環境構築編は[Selenide+Selenoid+GaugeでE2Eテストを自動化する【環境構築編】](https://qiita.com/nobutak/items/1eaa61e5323cacec6a6a)を参照してください。

## ディレクトリ構成

本サンプルは、以下の構成でファイルを準備します。

```
.
├── env                    # プロパティファイル
│   ├── browsers           # ブラウザプロパティ
│   │   ├── chrome
│   │   │   └── browser.properties
│   │   └── firefox
│   │       └── browser.properties
│   └── default            # デフォルトプロパティ
│       ├── default.properties
├── gauge                  # Gauge:ログ、レポート出力先
│   ├── logs
│   └── reports/html-report
├── selenoid               # Selenoid:設定、ログ、録画ファイル出力先
│   ├── config
│   │   └── browsers.json
│   ├── log
│   └── video
├── spec
│   └── sample.spec        # specファイル
├── src/test/java
│   ├── java               # テストコード
│   │   ├── pageobject     # ページオブジェクト
│   │   │   ├── GoogleSearchPage.java
│   │   │   └── GoogleSearchResultPage.java
│   │   ├── verification   # 検証オブジェクト
│   │   │   └── VerifyTitle.java
│   │   ├── Setup.java     # テスト前処理
│   │   └── Teardown.java  # テスト後処理
│   └── resources/csv      # リソース
│       └── sample.csv     # Gauge：動的パラメータファイル
├── manifest.json          # Gauge：マニフェストファイル
├── build.gradle           # Gradleビルドファイル
├── pom.xml                # Mavenビルドファイル
└── docker-compose.yml
```

## テストコードの準備

### browser.propertiesの作成

テスト実行時にブラウザを指定できるようにするため、ブラウザのプロパティファイルを作成します。
本サンプルではブラウザ名とバージョンを定義しています。

```env/browsers/chrome/browser.properties
browser.name = chrome
browser.version = 92.0
```

```env/browsers/firefox/browser.properties
browser.name = firefox
browser.version = 89.0
```

### テストコードの作成（前処理、後処理）

以下はテストスイート開始時に1度だけ呼ばれる処理になります。
ブラウザの指定やログ出力等の有効・無効を設定しています。

```java:src/test/java/Setup.java
import com.codeborne.selenide.WebDriverRunner;
import com.codeborne.selenide.Configuration;

import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import com.thoughtworks.gauge.BeforeSuite;

import java.net.URI;
import java.net.MalformedURLException;

public class Setup
{
    @BeforeSuite
    public void beforeSuite() throws MalformedURLException {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        // browser.propertiesよりブラウザ名とバージョンを取得し、設定する
        capabilities.setCapability("browserName", System.getenv("browser.name"));
        capabilities.setCapability("browserVersion", System.getenv("browser.version"));
        // Log出力：オン（selenoid/logsに出力される）
        capabilities.setCapability("enableLog", true);
        // VNC表示：オン（http://localhost:8081/#/のVNC表示を有効にする）
        capabilities.setCapability("enableVNC", true);
        // Video出力：オン（selenoid/videoに出力される）
        capabilities.setCapability("enableVideo", true);
        RemoteWebDriver driver = new RemoteWebDriver(
            URI.create("http://localhost:4445/wd/hub").toURL(),
            capabilities
        );
        WebDriverRunner.setWebDriver(driver);

        Configuration.fastSetValue = true;
    }
}
```

以下はテストスイート終了時に1度だけ呼ばれる処理になります。
ここではWebDriverのクローズ処理を行っています。

```java:src/test/java/Teardown.java
import com.codeborne.selenide.WebDriverRunner;

import com.thoughtworks.gauge.AfterSuite;

public class Teardown
{
    @AfterSuite
    public void afterSuite() {
        WebDriverRunner.closeWebDriver();
    }
}
```

### テストコードの作成（ページオブジェクト）

以下はGoogleの検索ページのページオブジェクトです。
Stepアノテーションにspecファイルで記述するテキストを指定します。Stepテキスト内で&lt;&gt;で囲まれた部分は変数で、specから与えられた値をメソッドのパラメータに渡します。この変数には文字列のほか、リソースファイル（CSVファイル）を渡すことができます。リソースファイル（CSVファイル）を参照する場合はTableオブジェクトを利用します。
※Stepテキストの変数名とメソッド引数の変数名で関連付けされるのではなく、Stepテキストに定義された変数の順番で先頭からメソッド引数に割り当てられます。

```java:src/test/java/pageobject/GoogleSearchPage.java
package pageobject;

import org.openqa.selenium.By;

import com.codeborne.selenide.SelenideElement;
import static com.codeborne.selenide.Selenide.$;
import static com.codeborne.selenide.Selenide.open;
import static com.codeborne.selenide.Selenide.page;

import com.thoughtworks.gauge.Step;
import com.thoughtworks.gauge.Table;
import com.thoughtworks.gauge.TableRow;

import java.util.List;

public class GoogleSearchPage
{
    private SelenideElement searchWord = $(By.name("q"));

    @Step("Google検索ページを開く")
    public GoogleSearchPage openPage() {
        return open("https://www.google.com/", GoogleSearchPage.class);
    }

    @Step("<text>と入力し、検索を行う")
    public GoogleSearchResultPage search(String text) {
        searchWord.val(text).pressEnter();
        return page(GoogleSearchResultPage.class);
    }

    @Step("<table>の検索ワードで検索し、公式ページを開く")
    public void searchOfficialPage(Table table) {
        List<TableRow> rows = table.getTableRows();
        for (TableRow row : rows) {
            openPage()
            .search(row.getCell("search_word"))
            .clickSearchResult(row.getCell("title"));
        }
    }
}
```

以下はGoogleの検索結果ページのページオブジェクトです。

```java:src/test/java/pageobject/GoogleSearchResultPage.java
package pageobject;

import com.codeborne.selenide.ElementsCollection;
import static com.codeborne.selenide.Selenide.$$;
import static com.codeborne.selenide.Selenide.page;
import static com.codeborne.selenide.Condition.matchText;

import com.thoughtworks.gauge.Step;

public class GoogleSearchResultPage
{
    private ElementsCollection searchResults = $$(".g h3");

    @Step("検索結果の<title>をクリックする")
    public void clickSearchResult(String title) {
        searchResults.findBy(matchText("^" + title + "$")).click();
    }
}
```

### テストコードの作成（検証オブジェクト）

検証コードをページオブジェクトから分離し、src/test/java/verification/に定義します。
以下はページタイトルを検証する検証オブジェクトです。
ページオブジェクトと同様、Stepアノテーションにspecファイルで記述するテキストを指定します。

```java:src/test/java/verification/VerifyTitle.java
package verification;

import static com.codeborne.selenide.Selenide.$;
import static com.codeborne.selenide.Condition.attribute;

import com.thoughtworks.gauge.Step;

public class VerifyTitle
{
    @Step("ページタイトルが<title>であること")
    public void verifyTitle(String title) {
        $("title").shouldHave(attribute("text", title));
    }
}
```

### リソースファイルの作成

Gaugeは、動的な入力値をCSVファイルで管理することができます。
specファイル内で&lt;table:src/test/resources/csv/sample.csv&gt;と記述することで、テストコードで簡単にCSVファイルを参照することができるため、入力フォームなどで繰り返しデータを入力する場合に便利です。

```src/test/resources/csv/sample.csv
search_word,title
selenide,Selenide: concise UI tests in Java
selenoid,Selenoid - A cross browser Selenium solution for Docker
```

### specファイルの作成

テストコードでStepアノテーションに記述したStepテキストを並べ、シナリオを記述します。Markdownのため、非常に分かりやすくテストケースを書くことができます。
Stepテキスト内の"変数値"や&lt;table:ファイルの相対パス&gt;で動的な値やリソースファイル（CSVファイル）をテストコードに渡すことができます。

```md:specs/sample.spec
# E2E Sample

## Search Selenide
Tags: search_selenide

* Google検索ページを開く
* "selenide"と入力し、検索を行う
* 検索結果の"Selenide: concise UI tests in Java"をクリックする
* ページタイトルが"Selenide: concise UI tests in Java"であること

## Search Selenoid
Tags: search_selenoid

* Google検索ページを開く
* "selenoid"と入力し、検索を行う
* 検索結果の"Selenoid - A cross browser Selenium solution for Docker"をクリックする
* ページタイトルが"Selenoid - A cross browser Selenium solution for Docker"であること

## Table Sample
Tags: table_sample

* <table:src/test/resources/csv/sample.csv>の検索ワードで検索し、公式ページを開く
```

## テストの実行

### テストコードのコンパイル

以下のコマンドでテストコードをコンパイルします。

```bash
# Gradleを利用する場合
$ ./gradlew clean test
# Mavenを利用する場合
$ mvn clean test-compile
```

### テストの実行

以下のコマンドでテストを実行します。
実行オプションでspecファイルやtagを指定することができます。
https://github.com/getgauge/gauge-gradle-plugin
https://github.com/getgauge-contrib/gauge-maven-plugin

本サンプルでは、-Penvや-Denvでbrowser.propertiesを指定していますが、複数のプロパティファイルを指定する場合はpropertyA,propertyBといった形でカンマ区切りで指定します。

```bash
# Gradleを利用する場合
# 全体実行
./gradlew clean gauge -Penv=browsers/chrome
# specファイル指定実行
./gradlew clean gauge -Penv=browsers/chrome -PspecsDir=specs/example.spec
# tag指定実行
./gradlew clean gauge -Penv=browsers/chrome -PspecsDir=specs/example.spec -Ptags="search_selenide | search_selenoid"

# Mavenを利用する場合
# 全体実行
mvn gauge:execute -Denv=browsers/chrome
# specファイル指定実行
mvn gauge:execute -Denv=browsers/chrome -DspecsDir=specs/example.spec
# tag指定実行
mvn gauge:execute -Denv=browsers/chrome -DspecsDir=specs/example.spec　-Dtags="search_selenide | search_selenoid"
```

テストを実行し、http://localhost:8081/#/ にアクセスすると、以下のようにセッションが作成されます。
![selenoid_ui_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1876352/5bb37153-8d49-7114-40cc-f989087f546c.png)
セッションをクリックすると、以下のように実行中のブラウザ操作を確認することができます。（java:src/test/java/Setup.javaでenableVNCをtrueに指定している場合のみ）
![selenoid_ui_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1876352/2430e191-c365-d78f-8747-856a323b5900.png)