Selenideで簡易UIテストを実現した話

Selenideを使って簡易的なUIテスト構築しました。

# そもそも
Selenideとは
https://selenide.org/

SeleniumのラッパーでUIテストを簡単に行うことができます。

# 何をしたか
指定したURLにアクセスして、
- URLが変わっていないか（リダイレクトしていないか）
- ある特定のタグがあるか（ないか） 
上記を確認して、ページがある程度正常に動いているのかを確認。

# 構成
Mac + homebrew
Selenide
IntelliJ + java11 + Maven
CIしたいサイト

## 最終的なプロジェクト構成

```
/pom.xml
/src/main * 使いません
/src/test/java/jp/hoge/TargetPage.java
/src/test/java/jp/hoge/Tester.java
/src/test/java/jp/hoge/uri.tsv
```

# 手順
## プロジェクト作成
まずMavenで

```bash
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=jp.hoge -DartifactId=hoge_test
```
*失敗した場合はmavenのプロキシなど確認して見てください。

できたら、pom.xmlに以下を追加。

```xml

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>selenide</artifactId>
            <version>5.0.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>
    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-report-plugin</artifactId>
                <version>2.22.1</version>
            </plugin>
        </plugins>
    </reporting>
```
*org.slf4jをいれないと途中でエラーしました。

## コーディング
今回はページオブジェクトという考え方を用いてテストコードを書いていこうと思います。
簡単にいうと、ページを一つのオブジェクトとしてあつかおうという考えです。

今回はある特定のタグがあるかどうかで、ページが正常かどうかを判断するので以下のようにして見ました。

###TargetPage
`divAlive`があることを期待しています。
今回はidやtagNameで指定しましたが、xpathやclass名でも指定できます。
参考：https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/FindBy.html

```java

package jp.hoge;

import com.codeborne.selenide.SelenideElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.FindBys;

public class TargetPage
{
    /**
     * タイトル要素(正常)
     */
    @FindBys({
            @FindBy(id = "alive"),
            @FindBy(tagName = "div")
    })
    public SelenideElement divAlive;
}

```

次にテストケースを作成。
今回はtsvで定義することにしました。

### url.tsv
左から、リダイレクトするかの期待値(0=false,1=true)、対象URLとしています。

```text

0	https://hoge.jp/
1	https://piyo.jp/
```

長くなりましたが、下記が本編。
テストケースを読み込んで、テストを実行してきます。

### Tester.java

```java

package jp.hoge;

import com.codeborne.selenide.WebDriverRunner;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.codeborne.selenide.Selenide.open;

@RunWith(Parameterized.class)
public class Tester {
    private int id;
    private boolean isRedirect;
    private String urlTarget;

    public Tester(
            int id,
            boolean isRedirect,
            String urlTarget
    ) {
        this.id = id;
        this.isRedirect = isRedirect;
        this.urlTarget = urlTarget;
    }

    @Parameterized.Parameters(name = "行数{0}:リダイレクト{1}:対象URL[{2}]")
    public static ArrayList<Object[]> getTestCase() {
        final String fileName = "uri.tsv";
        ArrayList<Object[]> testCases = new ArrayList<>();

        try {
            List<String[]> tsvData = Tester.getTsvData(fileName);

            for (int i = 0; i < tsvData.size(); i++) {
                String[] line = tsvData.get(i);
                if (line.length <= 1) {
                    continue;
                }

                Object[] testCase = new Object[3];
                // テストID
                testCase[0] = i + 1;
                // リダイレクトの期待
                testCase[1] = line[0].equals("1");
                // 対象のURL
                testCase[2] = line[1];
                testCases.add(testCase);
            }
        } catch (Exception e) {
            throw e;
        }

        return testCases;
    }

    private static ArrayList<String[]> getTsvData(final String filePath) {
        ArrayList<String[]> data = new ArrayList<>();

        try {
            //ファイルを読み込む
            FileReader fr = new FileReader(filePath);
            BufferedReader br = new BufferedReader(fr);

            //読み込んだファイルを１行ずつ処理する
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    break;
                }

                data.add(line.split("\t"));
            }

            //終了処理
            br.close();
        } catch (IOException ex) {
            //例外発生時処理
            ex.printStackTrace();
        }

        return data;
    }

    @Test
    public void doCheck() {
        try {
            // ページ取得を実行
            TargetPage page = open(this.urlTarget, TargetPage.class);

            // テキストなども指定可能
            if (!page.divAlive.exists()) {
                throw new Exception();
            }
        } catch (Exception e) {
            Assert.fail(this.id + ":画面表示失敗" + this.urlTarget);
        }

        // 現在の　URLを取得
        String url = WebDriverRunner.getWebDriver().getCurrentUrl();
        if (this.isRedirect) {
            Assert.assertNotEquals(this.id + ":リダイレクトしていません" + this.urlTarget, this.urlTarget, url);
            return;
        }
        Assert.assertEquals(this.id + ":リダイレクトしています" + this.urlTarget, this.urlTarget, url);
    }
}
```

## テスト実行
```bash
# プロジェクトディレクトリ以下で
mvn test
```

これで、対象のURLにアクセスし、`TargetPage` で定義したタグを取得。
タグの内容および、URLのチェックを自動的に行うことができます。

# 終わりに
今回は簡素的なタグおよびケースのチェックでしたが、タグの内容をもっと厳密にチェックしたり、クリックしたりなどもできます。簡易的なコードでは、ページオブジェクトやSelenideのうまさを活かしきれませんが、これが複雑になると威力を発揮します。