Selenideを活用したい人に向けて


#Selenideとは
Selenideとは、Seleniumをより進化させたWebの自動テストフレームワークです。
以下のような特徴があります。

##WebDriverを直接操作しない
Seleniumでは画面を開いたり、要素を探したりするのにWebDriverを直接操作する必要がありました。
しかし、SelenideではWebDriverをフレームワークで隠蔽しており、使用者がSelenideの単純なメソッドを呼び出すだけで
各操作を実現できるようになっています。

##Ajaxのサポート
Ajaxを使用しているアプリを操作するためには、各操作ごとに待機時間が必要です。
Seleniumでは、各操作をするごとに使用者がwaitを記述しないといけませんでしたが、Selenideではフレームワークが自動で待機してくれます。
デフォルトでは、最大4秒間待機するようになっています。
ただし、これはすべてのアクションで毎回4秒待機するというわけではなく、
Selenideで指定された要素が存在しない場合現れるまで4秒待機してみるというロジックになっています。


大きい特徴はこの2つですが、Seleniumより便利な機能が他にもたくさんありますので紹介していきます。

#実装方法
##設定
Selenideでは、実行する前にConfigurationクラスのpublic staticな変数に基本情報を設定して実行します。
Configurationクラスにて管理されているパラメータでよく使用すると思われるものをピックアップしました。

・browser
実行するブラウザの種類を設定します。設定する値は、WebDriverRunnerクラスに設定されています。
デフォルト値：firefox
また、この変数に設定しなくてもシステム変数「selenide.browser」に設定しても同様の効果があります。
例）

```java
// 使用するブラウザにChromeを設定します。
Configuration.browser = WebDriverRunner.CHROME;
```

・timeout
待機時間を設定します。
デフォルト値：4000
また、この変数に設定しなくてもシステム変数「selenide.timeout」に設定しても同様の効果があります。

・baseUrl
ベースとなるURLを設定します。この値を設定することで、画面を開くときに、base部分を省略して記述することができます。
デフォルト：http://localhost:8080
また、この変数に設定しなくてもシステム変数「selenide.baseUrl」に設定しても同様の効果があります。

・reportsFolder
スクリーンショットを取ったときのファイルやダウンロードしたファイルを保存するディレクトリを設定する。
デフォルト：build/reports/tests
また、この変数に設定しなくてもシステム変数「selenide.reportsFolder」に設定しても同様の効果があります。


##ページを開く
Selenideを使用する基本となるWEBページを開くには、Selenideのopen関数を使用します。
ベーシック認証がある場合でもopen関数は対応しています。

```java
// フルパスを指定
Selenide.open("https://www.yahoo.co.jp/");

// BaseUrlに指定したアドレスの相対パスを指定しても開ける
Configuration.baseUrl = "https://www.yahoo.co.jp/";
Selenide.open("/");
```

##要素の指定
Selenideを使用すると「$("#id")」のようにJquery風に要素を取得することができます。※Selenideをstaticインポートした場合
この方法以外にもXpathで指定することもできます。
Selenideでは、指定した要素が見つからなかった場合、最大Configration.timeoutに設定されたミリ秒分の間繰り返し探します。
これがSelenideを利用する一番のメリットかと思います。いちいち自分で待機処理を書かなくてよい！

```java
// JQuery風な取得方法
Selenide.$("#srchtxt");
// Xpath
Selenide.$By.xpath("//*[@id="srchtxt"]"));
```

##要素のアクション
上記で示した方法で要素を取得すると、SelenideElementクラスが返却されます。
SelenideElementクラスには、以下のようなメソッドを持っておりその要素に対して特定のアクションを行うことができます。
よく使用しそうなメソッドを抜粋。
####click()：
クリック
####doubleClick()：
ダブルクリック
####hover()：
ホバー
####setValue(String) / val(String)：
値の設定（value）
####pressEnter()：
Enterを押下
####pressEscape()：
Escapeを押下
####pressTab()：
タブを押下
####selectRadio(String)：
ラジオボタンに値を設定
####selectOption(String)：
プルダウンに値を設定
####download()：
要素の持っているhref属性に設定されているアドレスにアクセスしてファイルダウンロードします。
Fileを戻り値に持っているが、「Configration.reportsFolder」に設定したディレクトリにまず格納される。

##iframe、windowの変更
iframeを変更するためには以下のように実施します。

```java
Selenide.switchTo().frame("id");
```

switchToでロケータを取得して、frameメソッドを使用してiframeを変更します。
frameの引数には、idだけでなくnameやWebElement、インデックスも使用できます。
また、親のフレームに戻りたいときは、parentFrameメソッドを使用すると戻れます。

```java
Selenide.switchTo().parentFrame();
```

switchToで取得できるロケータは、iframeの変更以外にも
window/tab/frame/parentFrame/alertの変更に使用できます。

例えば、アクティブウィンドウの変更は以下のようにします。

```java
Selenide.switchTo().window("nameOrHandleOrTitle");
```

NameやTitleが分かっている場合は簡単だが、分からない場合はHandleを取得すると良い。
以下の方法でHandleは取得できる。

```java
// 現在アクティブなWindowのHandleを取得する
String handle = WebDriverRunner.getWebDriver().getWindowHandle();
// WebDriverから開いたすべての画面のHandleを取得する
Set<String> handles = WebDriverRunner.getWebDriver().getWindowHandles();
```

##待機
Selenideでは、フレームワーク側で待機してくれますが自身で待機処理をいれないと実施できないケースがあると思います。
例えば、ajaxにて値だけを書き換えたりする場合とか。そういったときに、以下のコマンドで待機が可能です。

```java
String preTableCation = Selenide.$("#tablecaption").getText();
WebDriverWait webDriverWait = new WebDriverWait(WebDriverRunner.getWebDriver(), 500);
webDriverWait.until(web -> {
    String caption = Selenide.$("#tablecaption").getText();
    return !StringUtils.equals(preTableCation, caption);
});
```

Selenium自体のクラスであるWebDriverWaitを使用して、この例の場合はtablecaptionの値が変わるまで待機しています。
WebDriverWaitには、よく使用するものとして以下の２つがあります。
WebDriverWait(WebDriver driver, long timeOutInSeconds)
WebDriverWait(WebDriver driver, long timeOutInSeconds, long sleepInMillis)
timeOutInSecondsは、最大何秒間待機するのかです。過ぎた場合は、TimeoutExceptionをthrowします。
sleepInMillisは、何ミリ秒間隔で監視を行うのかです。

よく見たら、waitUntilというメソッドがWebElementに準備されていました。
上記と同じ処理を書いてみました。

```java
Selenide.$("#tablecaption").waitWhile(Condition.exactValue(preTableCation), 5);
```

こちらのほうがかなりシンプルに書けますね。
また、Conditionクラスには、エレメント状態を判定する関数を準備してくれているので見てみると良いと思います。

##ページオブジェクトパターン
ページオブジェクトパターンとは、１つのページを１オブジェクトとして考えるデザインパターンの考え方です。
Javaでいうと１ページ１クラスで作成します。この方法は、Seleniumの公式でも推奨されているテストコードの書き方です。
どのような手法かというと、テストコードとページの要素指定などの動きを分離する手法です。
具体例を見るとわかりやすいと思います。

```java:実行クラス
public class SampleTest {

    /** 接続パスを設定する **/
    private static final String URL = "http://yahoo.co.jp/";

    @Before
    private void before() {
        // ブラウザは、Chromeを設定する
        Configuration.browser = WebDriverRunner.CHROME;
        // タイムアウト時間を設定する（デフォルトは4秒）
        Configuration.timeout = 1000 * 60 * 3;
        // BaseURLを設定する
        Configuration.baseUrl = URL;
        // ドライバの場所を指定する
        System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, "driver/chromedriver.exe");
    }

    /**
     * 検索
     * @throws IOException
     */
    @Test
    public void start() throws IOException {
        YahooPage samplePage = Selenide.open("/", YahooPage.class);
        SearchList searchList = samplePage.search("検索条件");
    }
}
```

```java:Pageクラス
public class YahooPage {

    // 天気
    @FindBy(xpath="//*[@id='navi']//a[contains(text(), '天気')]")
    private SelenideElement weather;

    // 検索テキストボックス
    @FindBy(id="srchtxt")
    private SelenideElement srchtxt;

    /**
     * 検索
     * @return 検索結果ページ
     */
    public SearchList search(final String srhText) {
        srchtxt.val(srhText).pressEnter();
        return page(SearchList.class);
    }

    /**
     * 天気をクリック
     * @return 天気ページ
     */
    public WeatherPage clickWeather() {
        weather.click();
        return page(WeatherPage.class);
    }
}

public class SearchList {

    // 検索一覧
    @FindBy(xpath="//*[@id='WS2m']/div")
    private ElementsCollection searchList;

}

public class WeatherPage {

    // 大阪
    @FindBy(xpath="//*[@id='map']//*[contains(text(), '大阪')]/../../../a")
    private SelenideElement weather;
}
```

このようにページごとのクラスを準備して、そこに各ページでのアクション、エレメントを定義します。
そして、テストコードではそのアクションを実行して結果を得るようにします。
そうすることによって、テストコードとページ処理が分離したメンテナンスしやすいコードが出来上がります。

各ページのエレメントの取得には、アクションの際にSelenideの$メソッドを使用して取得する方法もありますが、
FindByアノテーションを使用してクラスのprivate変数に設定することもできます。
アノテーションを利用する方法では、メソッドとエレメントの分離ができますのでより可読性の高い記述になるでしょう。
FindByアノテーションでは、xpath以外でもcss、id、name等の指定が可能です。
FindByアノテーションでデコレートした変数に値を注入するには、Selenideのopenメソッドもしくは、pageメソッドを利用してください。

##検証方法
Selenideには、Junitに対応した検証方法が多く追加されています。
検証するときにいちいち値を取得してassertしてとかをしなくても良いです。

```java:例
Selenide.$("#password").shouldHave(Condition.text("Welcome, user!"));
```

#最後に
Selenideは非常に便利で、Seleniumよりも可読性もメンテナンス性も高いコードを記述することができます。
みなさんどんどん使用していきましょう！