WebDriverManagerを使用してgeckodriver(SeleniumのWebDriver)を自動インストールする方法

前回「[geckodriver(SeleniumのWebDriver)をMavenコマンド使ってパス上に配置する方法](http://qiita.com/kazuki43zoo/items/093a25ddfc93b532512b)」（やや強引なドリブルをするやり方）を紹介しましたが・・・WebDriver毎の実行ファイルをダウンロードしてSeleniumが参照可能なパスへ自動でインストールしてくれるライブラリ（[WebDriverManager](https://github.com/bonigarcia/webdrivermanager)）があったので、今回はそちらの使い方を紹介したいと思います！！

> **Note:**
>
> 2017/4/2 :追記
>
> 1.6.2がリリースされたので記載を修正しました。1.6.2より、認証付きProxy Serverを介したインターネットアクセスがサポートされました！！

# 検証バージョン

* WebDriverManager 1.6.2
* Selenide 4.3
* Selenium 3.0.1 (Selenide 4.3の依存バージョン)
* geckodriver 0.14.0
* Firefox 52.0.1 (64 ビット)
* IntelliJ IDEA 2016.3.5
* OS: Mac

# 実践アプリ

本エントリーで記載した内容は、以下のアプリケーションで実践しています。以降の説明も、このアプリケーションをベースに説明していきます。

* https://github.com/kazuki43zoo/mybatis-spring-boot-jpetstore/tree/webdrivermanager

> Note:
>
> 原因はわかっていませんが、たまにテストが失敗することがあります・・・（時間がある時に調査してみます） :sweat_smile:

# WebDriverManagerってなにもの？

具体的な使い方を紹介する前に、WebDriverManagerが何をしてくれるのか簡単に紹介しておきましょう。WebDriverManagerは、具体的には以下の２つの処理を行います。

* `WebDriver`が使用する実行ファイルのダウンロード＆アーカイブファイルの解凍(所定ディレクトリへの配置)
* 実行ファイルのパスを指定するシステムプロパティ(例：`webdriver.gecko.driver`)の設定

> Note:
>
> デフォルトでは、テスト実行時の最新バージョンがインストールされます。すでに同一バージョンの実行ファイルがインストールされていればダウンロード処理はスキップし、「実行ファイルのパスを指定するシステムプロパティ」の設定処理だけ行われます。


## 実行ファイルの配置先

デフォルトでは、実行ファイルは「\$HOME/.m2/repository/webdriver」配下に「\$HOME/.m2/repository/webdriver/{ドライバ名}/{OS}/{バージョン}/{実行ファイル名}」というルールで格納されます。以下は、Forefoxを使う際に必要になる`geckodriver`の配置例になります。

```
$ pwd
/Users/xxx/.m2/repository/webdriver
$ tree
.
└── geckodriver
    └── macos
        └── 0.14.0
            └── geckodriver // ### 実行ファイル
```


## サポートしているWebDriver実装

WebDriverManagerがサポートしているのは、以下の6つのWebDriver実装になります。

| WebDriverクラス名 | 実行ファイルのパスを指定するシステムプロパティ |
| -------- | ------------------ |
| `ChromeDriver` | `webdriver.chrome.driver` |
| `OperaDriver` | `webdriver.opera.driver` |
| `InternetExplorerDriver` | `webdriver.ie.driver` |
| `EdgeDriver` | `webdriver.edge.driver` |
| `PhantomJSDriver` | `phantomjs.binary.path` |
| `FirefoxDriver` | `webdriver.gecko.driver` |

> Note:
>
> Safariはサポートしていないみたいです・・・。

# WebDriverManagerのインストール

WebDriverManagerのインストールは、WebDriverManagerのjarファイルをクラスパスに追加するだけです。以下は、Maven使用時のインストール方法になります。

```xml:pom.xml
<dependency>
	<groupId>io.github.bonigarcia</groupId>
	<artifactId>webdrivermanager</artifactId>
	<version>1.6.2</version> <!-- 投稿時の最新バージョン -->
	<scope>test</scope>
</dependency>
```

# WebDriverManagerのセットアップ

WebDriverManagerは勝手に実行ファイルをインストールすることはありません。実行ファイルを自動でインストールしたい場合は、使用するWebDriver実装に対応するManagerクラスのメソッドを明示的に呼び出す必要があります。

```java
// ...
import static com.codeborne.selenide.Configuration.*;
// ...
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class JpetstoreApplicationTests {

	@LocalServerPort
	private int port;

	@BeforeClass
	public static void setupWebDriverManager() {
		FirefoxDriverManager.getInstance().setup(); // (1) 
	}

	@Before
	public void setupSelenide() {
		browser = MARIONETTE; // (2)
		timeout = TimeUnit.SECONDS.toMillis(10);
		baseUrl = String.format("http://localhost:%d", port);
	}

	// ...

}
```

| 項番 | 説明 |
| ---- | --- |
| (1) | WebDriver実装毎のManagerクラスに実装されている`setup`メソッドを呼び出すと、WebDriverの実行ファイルがインストールされる。ここでは、FirefoxDriverを使う際に必要なgeckodriverをインストールしている。 |
| (2) | Selenideが使用するブラウザ(WebDriver実装)の指定を行う。ここでは、FirefoxDriver(MARIONETTE)を指定している。 |

# テストを実施してみる！！

テストケースを実行すると、以下のようなログが出力され、テスト実施前にWebDriverの実行ファイルがインストールされていることがわかります。

```
...
21:53:23.107 [main] INFO  io.github.bonigarcia.wdm.Downloader - Downloading https://github.com/mozilla/geckodriver/releases/download/v0.14.0/geckodriver-v0.14.0-macos.tar.gz to /Users/xxx/.m2/repository/webdriver/geckodriver/macos/0.14.0/geckodriver-v0.14.0-macos.tar.gz
21:53:31.084 [main] INFO  i.g.bonigarcia.wdm.BrowserManager - Exporting webdriver.gecko.driver as /Users/xxx/.m2/repository/webdriver/geckodriver/macos/0.14.0/geckodriver
...
```

デフォルトでは、テスト実行時の最新バージョンがインストールされますが、場合によっては最新バージョンだと正しく動作しないことがあります。本エントリで利用しているSelenide(Seleniumのラッパーライブラリ)だとgeckodriverの0.15.0(投稿時の最新版)だと正しく動作しないため、意図的にひとつ前の0.14.0を使用するようにしています。

ちなみに・・・1.6.2からHTTP通信は[Apache Components HttpClient](https://hc.apache.org/httpcomponents-client-4.5.x/index.html)を使うよう変更になっています。実践アプリでは`@BeforeClass`を付与したメソッドで`setup`メソッドを呼び出している関係で、HttpClientの通信ログが大量に出力されるため、テスト用の`logback.xml`(`logback-test.xml`)を用意しています。

```xml:src/test/resources/logback-test.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
      <charset>UTF-8</charset>
    </encoder>
  </appender>

  <logger name="org.apache.http" level="error"/>

  <root level="info">
    <appender-ref ref="CONSOLE"/>
  </root>
</configuration>
```

# WebDriverManagerのコンフィギュレーション

WebDriverManagerの動作は、[Java API](https://github.com/bonigarcia/webdrivermanager#webdrivermanager-api)と[プロパティファイル](https://github.com/bonigarcia/webdrivermanager#configuration)(クラスパス直下の`webdrivermanager.properties`)を使用してコンフィギュレーションすることができます。
たとえば、使用するバージョンを固定化したい場合は・・・

* Java APIの場合

```java
@BeforeClass
public static void setupWebDriverManager() {
	FirefoxDriverManager.getInstance().version("0.14.0").setup();
}
```

* プロパティファイルの場合

```properties
wdm.geckoDriverVersion = 0.14.0
```

といった感じで、動作をカスタマイズすることができます。

# GitHub API使用時に発生する403エラーの回避方法

WebDriver実装としてOperaDriverまたはFirefoxDriverを使用する場合は、実行ファイルをダウンロードする際にGitHub APIを使用する仕組みになっている関係で、IPアドレス単位の「API呼び出し回数制限」にひっかかり、403エラーが発生して実行ファイルがダウンロードができないケースがあります。時間をおけば制限が解除されダウンロードすることができますが、この事象を根本的に解決したい場合は、GitHub APIを呼び出す際にBasic認証を使用してGitHubアカウントの資格情報（ユーザ名とトークン or パスワードを指定してください。
ちなみに・・・セキュリティ的な観点から**トークン**を使うようにしましょう。なお、トークンは「[New personal access token](https://github.com/settings/tokens/new)」で発行することができます（スコープは選択しなくてもOK）。

で・・・GitHubのユーザ名と発行したトークンは、

* システムプロパティ
* プロパティファイル(`webdrivermanager.properties`)

のいずれかに指定します。それぞれプロパティキーは以下の通りです。

| プロパティキー | 説明 |
| ----------- | ---- |
| `wdm.gitHubTokenName` | GitHubのアカウント名 |
| `wdm.gitHubTokenSecret` |  「[New personal access token](https://github.com/settings/tokens/new)」で発行したトークン |

おそらくプロパティファイルはGitなどのVCSで管理することが多いと思うので、セキュリティの観点からいうとプロパティファイルではなく**システムプロパティとして指定する方がよい**と思います。

本エントリーで紹介している実践アプリでも、Travis CI上でテストを実行する際には、Travis CI上の環境変数に資格情報を設定しておき、テスト実施を行うmvnコマンド実行時に環境変数から取得した値をシステムプロパティを介して連携するようにしています。

```
./mvnw -U package -Dwdm.gitHubTokenName=${WDM_GITHUB_USER} -Dwdm.gitHubTokenSecret=${WDM_GITHUB_TOKEN}
```

# Proxy Serverを介したインターネット接続

インターネットに接続する際にProxy Serverを介す必要がある場合は、「HTTPS_PROXY」環境変数にProxy Serverの接続情報を指定する方法と、Java APIを使用して指定する方法が用意されており、

* 「HTTPS_PROXY」環境変数の場合

```
export HTTPS_PROXY=http://proxy.example.com:9999
```

* Java APIの場合

```java
@BeforeClass
public static void setupWebDriverManager() {
	FirefoxDriverManager.getInstance().proxy("http://proxy.example.com:9999").setup();
}
```

といった感じで指定することができます。

> **Note: 認証付きのProxy Serverを介したインターネット接続について**
>
> 1.6.2から認証付きProxy Serverを介したインターネット環境でもWebDriverManagerのダウンロード機能がつかえるようになり、Proxy Serverのユーザ名とパスワードの指定は、以下の中から選ぶことができます。
>
> * `HTTPS_PROXY`環境変数の設定値の中で指定。形式：「`http://username:password@host:port`」
> * `HTTPS_PROXY_USER`と`HTTPS_PROXY_PASS`環境変数に指定
> * `BrowserManager`の`proxy(String)`、`proxyUser(String)`、`proxyPass(String)`メソッドの引数に指定

# まとめ

前回「[geckodriver(SeleniumのWebDriver)をMavenコマンド使ってパス上に配置する方法](http://qiita.com/kazuki43zoo/items/093a25ddfc93b532512b)」（やや強引なドリブルをするやり方）を紹介してしまいましたが、WebDriverManagerを使用して実行ファイルをSeleniumにインストールする（認識させる）のがよさげです。1.6.2から「認証付きのProxy Serverを介したインターネット接続」がサポートされたので、職場環境への導入のハードルがなくなった！！


# 参考サイト

* https://github.com/bonigarcia/webdrivermanager