JShellを使ってSeleniumで対話的にブラウザ操作を行う

# はじめに

今回はJavaでSeleniumを使う際にJava9で導入された対話環境(いわゆるREPL)のJShellを使ってみようと思います。

# なぜREPLか

Selenium WebDriverで自動テストを記述する際、皆さんはDOM要素の取得をどのように行っているでしょうか。大抵の場合、各ブラウザの開発者ツールで対象ページを開き、`id`のように一意に取得できるものを探しそれをもとにコードに起こしているでしょう。
ですが複雑なDOM構造や`id`が振られていない場合どうするでしょう。DOM構造からあたりをつけて、それをもとにコードを書き試しに実行し意図通りに取れたか`printf`デバックを行って……ということもあるのではないでしょうか。

DOMをたどっていく場合やWebDriverのラッパー固有のAPIを用いる場合、実際に動かしてみないと意図通りに要素取得ができるかまた操作時の振る舞いも意図通りか検証できないことがあります。実行するにしてもテストシナリオの後ろの方にあると、確認するだけでも時間がかかり非常に効率が悪くなってしまいます。

その解決手段の一つとして、言語毎に提供されている`REPL`のような対話環境を用いるということがあります。REPLでSeleniumを動かしブラウザを起動させ目的の画面までは手動で操作し、コードの確認をREPLで行うというものです。
Javaには最近までREPLがなかったため、代替手段としてIDEのデバッグ機能を使い疑似的に対話環境を使うということを筆者は行ってきました。ですが、Java9からは公式REPLである`JShell`が導入されたためこれを使い対話的に操作する方法を解説していきます。

# 前提条件

この記事で書くことを試すには以下の環境が必要です。

|ソフトウェア|バージョン等|備考|
|:--|:--|:--|
|Oracle JDK|9以上|JShellを動かすため|
|Apache Maven|3.3.9|jshell-maven-pluginの条件|
|IntelliJ IDEA|2017.3以降|JShell Consoleのため|

なお筆者は以下の環境で検証しました。

|ソフトウェア|バージョン等|
|:--|:--|
|Oracle JDK|10|
|Apache Maven|3.5.2|
|IntelliJ IDEA Ultimate|2018.1|

# その1 IntelliJ IDEAのJShell Consoleを使う

一番簡単なのは、IntelliJ IDEAに内蔵されている`JShell Console`を使う方法です。
IntelliJ IDEAの[Tool]-[JShell Console]を起動させます。以下のような感じです。
<img width="501" alt="intellij_jshellconsole.png" src="https://qiita-image-store.s3.amazonaws.com/0/132479/6c2e2af7-9dc2-31aa-39b7-970325e94280.png">

起動させると以下のようなエディタが開きます。
<img width="1057" alt="jshell.png" src="https://qiita-image-store.s3.amazonaws.com/0/132479/617e56c5-c3a5-d810-21fc-070d47dd1862.png">
ここで入力したあとで「▶」を押下すると評価されます。部分的に評価したい場合はその範囲を選択しておくことで、その範囲のみが対象となります。

## 動かしてみる

それでは試しに実行してみましょう
https://github.com/shimashima35/codezine-digital-sample をCloneすると、pom.xmlでSelenideの設定がされています。このプロジェクトを開いた以上帯でJShell Consoleを起動し、以下のコードを入力し評価します。

```Java
import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.WebDriverRunner;

import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Selectors.*;
import static com.codeborne.selenide.Selenide.*;

Configuration.browser = WebDriverRunner.FIREFOX;
open("https://www.google.com")
```
そうすると、Firefox Driverをダウンロード、Firefox起動、Googleへのアクセスが行われそこで止まります。
この状態まで行けばあとはこのJShell Consoleで任意のコードを記述し、そのたびに評価を実行することで対話的にSelenium(この場合はSelenide)を動かすことができます。


# その2 [jshell-maven-plugin](https://github.com/bitterfox/jshell-maven-plugin) を使う

jshell-maven-pluginとは、JavaコミッターでJShellの開発を行っていた[@bitter_fox](https://twitter.com/bitter_fox)さんが開発したmaven pluginです。
JShellの起動時にclasspathを設定するのは意外に面倒です。プロジェクト自体がmavenをベースとしているのであれば、このpluginを使うことでpomに記載された設定をもとにJShellを使うことができます。

## 導入方法
jshell-maven-pluginは、この記事の執筆時点(2018/04/06)でまだCentral Repositoryにあがっていません。そのため、導入にはソースからビルドしてローカルもしくはインハウスリポジトリへのインストールが必要になります。

手順は前述の公式サイトのREADME.mdに記載がある通りで以下のようにJDK9に対して`JAVA_HOME`環境変数を設定したのちにインストールを実行します。

```sh
JAVA_HOME=/path/to/your/jdk9 mvn clean install
```
これでローカルリポジトリにはいります。

## 設定

pluginを使うために、`pom.xml`へ下記を追加します。

```xml
    <build>
        <plugins>
            <plugin>
                <groupId>net.java.openjdk.shinyafox</groupId>
                <artifactId>jshell-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
            </plugin>
        </plugins>
    </build>
```

## 実行

`JAVA_HOME\bin`にコマンドパスが通っていれば以下で起動します。

```sh
mvn jshell:compile
```
実行後以下のように標準出力が行われ待ち状態になります。

```sh
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building codezine 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- jshell-maven-plugin:1.0-SNAPSHOT:compile (default-cli) @ codezine ---
[INFO] Starting jshell with [
中略
]
|  JShellへようこそ -- バージョン10
|  概要については、次を入力してください: /help intro

jshell>
```

これ以降、`pom.xml` で依存を設定されたjarへのクラスパスが通った状態でjshellが使えます。

# 終わりに

JavaでもREPLが導入されSeleniumの操作も対話的に行うことができるようになりました。
静的言語としての強みを生かしつつ、REPLを使って効率的に開発を行うことが今まで以上に可能になったのではないでしょうか。
是非みなさんも一度試してみてください。