Java製SeleniumラッパーのSelenideを使ってみた


[Selenium勉強会＠サイボウズに行ってきました #selenium_cybozu](http://nowsprinting.hatenablog.com/entry/2015/10/22/124844)
の記事で

> Selenideの話
>　Selenide == Javaで書かれたSeleniumクライアントラッパー

というのを見つけてGroovy製のGebと比較したいなと思い、使ってみました。

### Selenideについて

公式はここ。http://selenide.org/

> Selenide is a wrapper for Selenium WebDriver that brings the following advantages:
> 
Concise API for tests 
Ajax support 
True Page Objects 
jQuery-style selectors
> 
You don't need to think how to shutdown browser, handle timeouts or write monstrous code!
Concentrate on business logic!

`gradle dependencies` を使って、Selenideの依存関係を確認してみると各種driverも入ってることがわかる。

```
+--- com.codeborne:selenide:2.23
|    +--- com.google.guava:guava:18.0
|    +--- org.seleniumhq.selenium:selenium-java:2.47.1
|    |    +--- org.seleniumhq.selenium:selenium-chrome-driver:2.47.1 -> 2.48.2
|    |    |    \--- org.seleniumhq.selenium:selenium-remote-driver:2.48.2
|    |    |         +--- cglib:cglib-nodep:2.1_3
|    |    |         +--- com.google.code.gson:gson:2.3.1
|    |    |         +--- org.seleniumhq.selenium:selenium-api:2.48.2
|    |    |         |    +--- com.google.guava:guava:18.0
|    |    |         |    +--- com.google.code.gson:gson:2.3.1
|    |    |         |    \--- org.apache.httpcomponents:httpclient:4.5.1 (*)
|    |    |         +--- org.apache.httpcomponents:httpclient:4.5.1 (*)
|    |    |         +--- com.google.guava:guava:18.0
|    |    |         +--- org.apache.commons:commons-exec:1.3
|    |    |         +--- net.java.dev.jna:jna:4.1.0
|    |    |         \--- net.java.dev.jna:jna-platform:4.1.0
|    |    |              \--- net.java.dev.jna:jna:4.1.0
|    |    +--- org.seleniumhq.selenium:selenium-edge-driver:2.47.1
|    |    |    +--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
|    |    |    +--- commons-io:commons-io:2.4
|    |    |    \--- org.apache.commons:commons-exec:1.3
|    |    +--- org.seleniumhq.selenium:selenium-firefox-driver:2.47.1
|    |    |    +--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
|    |    |    +--- commons-io:commons-io:2.4
|    |    |    \--- org.apache.commons:commons-exec:1.3
|    |    +--- org.seleniumhq.selenium:selenium-ie-driver:2.47.1
|    |    |    +--- net.java.dev.jna:jna:4.1.0
|    |    |    +--- net.java.dev.jna:jna-platform:4.1.0 (*)
|    |    |    \--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
|    |    +--- org.seleniumhq.selenium:selenium-support:2.47.1
|    |    |    \--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
|    |    \--- org.seleniumhq.selenium:selenium-leg-rc:2.47.1
|    |         \--- org.seleniumhq.selenium:selenium-remote-driver:2.47.1 -> 2.48.2(*)
|    \--- commons-codec:commons-codec:1.10

```



## 環境

selenide2.23
Java8
SpringBoot1.2.7
Doma2.5.0
H2
Gradle2.8

「[SpringBoot+Doma2+Gradleを試してみた。](http://qiita.com/nyasba/items/1e22c2401f3849f9071d)」のアプリを、ライブラリを最新化して使いまわしてます。

参考までにGebで実装したものはこちら。
「[SpringBootで作ったRestAPI/Webアプリのテストを書いてみた](http://qiita.com/nyasba/items/df826214ddaee0194a56)」

## 実装

ソースはこちら。
https://github.com/nyasba/domaboot.git

Gebのときと同様PageObjectパターンで実装してみました。

### メインページのPageObject

お作法はGebと微妙に違いますが、そんなに困ることなく移植できました。

```MainPage.java
package com.example.web.selenide.page;

import com.codeborne.selenide.Selenide;
import org.openqa.selenium.By;

/**
 * メインページ
 */
public class MainPage {

    private static final String URL = "http://localhost:8888/customers";

    public static void open(){
        Selenide.open(URL);
    }

    public static String title(){
        return Selenide.title();
    }

    public static int 登録件数(){
        return Selenide.$$(Selenide.$(By.id("customer-list")).find("tbody"),"tr").size();
    }

    public static void 姓は(String lastName){
        Selenide.$(By.id("lastName")).setValue(lastName);
    }
    public static void 名は(String firstName){
        Selenide.$(By.id("firstName")).setValue(firstName);
    }

    public static void で登録する(){
        Selenide.$(By.id("register")).click();
    }

    public static String 名前(int index){
        return Selenide.$(By.id("lastName" + String.valueOf(index))).getText()
                +  Selenide.$(By.id("firstName" + String.valueOf(index))).getText();
    }

    public static void 編集ボタンを押す(int index){
        Selenide.$(By.id("edit" + String.valueOf(index))).click();
    }

    public static void 削除ボタンを押す(int index){
        Selenide.$(By.id("delete" + String.valueOf(index))).click();
    }

}
```

### 編集ページのPageObject

```EditPage.java
package com.example.web.selenide.page;

import com.codeborne.selenide.Selenide;
import org.openqa.selenium.By;

/**
 * 編集ページ
 */
public class EditPage {

    public static String title(){
        return Selenide.title();
    }

    public static void 姓は(String lastName){
        Selenide.$(By.id("lastName")).setValue(lastName);
    }
    public static void 名は(String firstName){
        Selenide.$(By.id("firstName")).setValue(firstName);
    }

    public static void で更新する(){
        Selenide.$(By.id("submit")).click();
    }

    public static void やっぱり戻る(){
        Selenide.$(By.id("goToTop")).click();
    }
}
```

### テストシナリオ

流れ。

1. @BeforeClassでテスト対象アプリを起動し、Chromeを使う設定を追加
    (デフォルトはFirefoxになり、Firefoxだとdriverの設定は不要）
2. @Testでテスト実行
3. @Afterでテストで登録したデータをすべて削除
4. @AfterClassでブラウザを閉じる



```CustomerWebTest.java
package com.example.web.selenide;

import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.WebDriverRunner;
import com.example.App;
import com.example.web.selenide.page.EditPage;
import com.example.web.selenide.page.MainPage;
import org.junit.*;
import org.junit.runners.MethodSorters;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class CustomerWebTest {

    @BeforeClass
    public static void setUpClass() {

        // テスト対象アプリの起動
        App.main(new String[]{
                "--server.port=8888",
                "--spring.profiles.active=test"
        });

        // 何も指定しない場合はFirefoxになる
        Configuration.browser = WebDriverRunner.CHROME;
        System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe");
    }

    @After
    public void tearDown() {
        // 登録されてるデータを全部消す
        for(int i = MainPage.登録件数(); i > 0; i--){
            System.out.println("削除" + String.valueOf(i));
            MainPage.削除ボタンを押す(i);
        }
    }


    @AfterClass
    public static void tearDownClass() {
        WebDriverRunner.closeWebDriver();
    }

    @Test
    public void 剛田たけしとどらえもんを登録する() {
        MainPage.open();
        assertThat(MainPage.登録件数(), is(0));

        MainPage.姓は("剛田");
        MainPage.名は("たけし");
        MainPage.で登録する();

        assertThat(MainPage.登録件数(), is(1));
        assertThat(MainPage.名前(1), is("剛田たけし"));

        MainPage.姓は("どら");
        MainPage.名は("えもん");
        MainPage.で登録する();

        assertThat(MainPage.登録件数(), is(2));
        assertThat(MainPage.名前(2), is("どらえもん"));

    }

    @Test
    public void 剛田たけしで登録した後にきれいなジャイアンに変更する() {
        MainPage.open();
        MainPage.姓は("剛田");
        MainPage.名は("たけし");
        MainPage.で登録する();

        MainPage.編集ボタンを押す(1);

        EditPage.姓は("きれいな");
        EditPage.名は("ジャイアン");
        EditPage.で更新する();

        assertThat(MainPage.title(), is("顧客管理システム"));
        assertThat(MainPage.名前(1), is("きれいなジャイアン"));
    }

    @Test
    public void 剛田たけしで登録した後に変更しようとしてやっぱり戻る() {
        MainPage.open();
        MainPage.姓は("剛田");
        MainPage.名は("たけし");
        MainPage.で登録する();

        MainPage.編集ボタンを押す(1);

        EditPage.やっぱり戻る();

        assertThat(MainPage.名前(1), is("剛田たけし"));
    }

    @Test
    public void 剛田たけしで登録した後に削除する() {
        MainPage.open();
        MainPage.姓は("剛田");
        MainPage.名は("たけし");
        MainPage.で登録する();

        assertThat(MainPage.登録件数(), is(1));
        MainPage.削除ボタンを押す(1);

        assertThat(MainPage.登録件数(), is(0));
    }
}
```

Gebでは各テストをつなげて1つのテストシナリオにしていたのですが、JUnitでテストの順序を指定する方法がうまくいきませんでした。（@FixMethodOrderとか試したのですが）

ただ、そこは本質じゃないので、それぞれのテストを独立したものに書き換えたことによりGebと構成は少し変わってます

## 感想

SelenideはJava製ということがあって、Javaエンジニアの方が気軽に使えるというのが一番いいところですね。Groovy(Spock)の知識が必要なGebと比べると敷居は低いと思います

簡単なところまでしか試していませんが、機能的にも遜色ないとおもいます。

（もしかすると何か解決方法があるのかもしれませんが）Gebだとページが遷移していくときにIDEの補完機能が効かないときがあり、Selenideはそういうことはありませんでした。

＃個人的にはSpock+SelenideでCustomerWebTestの可読性をあげるのもアリかなと思いました。

おわり。