Selenium(Selenide)で実行時の録画を行う

# はじめに

この記事は[Selenium/Appium Advent Calendar 2017](https://qiita.com/advent-calendar/2017/selenium)の19日目の記事です。

皆さんSelenium(Selenide)でのテスト実行時にエラーとなった原因追及としてどのような方法を用いているでしょうか。ログ、画面キャプチャ、そして録画。この中でE2Eテストの不安定さを追うのに一番便利なのは録画ではないでしょうか。

今回はSelenium(Selenide)で実行時に録画を行う方法について説明します。

# お詫び

今回ご紹介する方法で録画したファイルにつきまして以前macOS上で再生ができたのですが、今回の記事執筆にあたって手元のWindowsで試したところ再生出来ませんでした。
macOSでは再生可能かと思うので、どなたか試して動作報告をいただけると助かります。

# 録画の目的

さて、Seleniumで録画をする目的は何でしょうか、「はじめに」でも書いたようにエラーの追求です。私がよく使っているSelenideのように、Seleniumのラッパーライブラリには大抵エラー時にそのとき表示していた画面キャプチャやHTMLを取得する仕組みが備わっています。これはこれで非常に便利なのですが、タイミングに起因するテストの不安定さを減らすにはこれだけでは不足なことがあります。その際、実行時の録画があればと感じることがあるのではないでしょうか。

テスト失敗時に原因追及が目的であれば、テストが成功したものについては録画が不要です。不必要な録画を減らすことで再生対象を自動的に絞ることができますしストレージ容量も節約出来ます。録画ファイルは比較的大きいので、Jenkinsなどで実行し続けているとストレージを圧迫する要因になるので意外に馬鹿になりません。

ということで、今回録画機能を組み込むにあたっては失敗時のみ残す仕組みも入れてみようと思います。

なお、この録画機能および失敗時のみ残すというアイディアは、Cookpadの[松尾さん](https://twitter.com/Kazu_cocoa)の講演によるものです。松尾さん、ありがとうございます。

# 録画機能実装

## 参考URL

今回の機能実装について以下のURLのコードを参考にしています。
https://roadtoautomation.wordpress.com/2013/03/28/screen-recording-video-of-java-webdriver-script/
https://memorynotfound.com/selenium-record-video-junit-java/

失敗時のみ残す、以外はほぼそのままです。

## 録画ライブラリ

さて、まず最初に録画を行うにはなにかしらのライブラリやツールが必要になってきます。
松尾さんの事例ではテストコードはRuby、録画ツールはPythonで、Rubyのテストコードからコマンドを叩いて録画の開始終了制御をしていたと記憶しています。この方法だと、プログラム内から外部コマンド起動という仕組み上の不安定さと、(コマンド有無などの)外部環境に依存するため実行環境の構築・維持にコストが掛かってしまいます。そのため、出来うる限りJavaで実装されたライブラリを探すことにしました。

Javaで画面レコーディングを行うためのライブラリは当時調べてみたもののほぼなく、唯一といってよいものが[Monte Media Library](http://www.randelshofer.ch/monte/)でした。
今回はこれを使います。

## Monte Media Libraryの使い方

Monte Media Libraryを用いた録画方法は先にあげた参考URLそのままです。
残念なことにMonte Media Libraryは公式サイトにも詳細な使い方・APIドキュメントが見つからなかったので手を入れることが出来ませんでした。
Monte Media Libraryのソースを追えばよかったのでしょうが、さすがにそこまでの気力はありませんでした。

## 失敗時のみ残す処理

次に録画失敗時のみ残す処理です。
これは、録画自体は常に行いテスト成功時には削除することで実現します。[JUnit4のRule](http://junit.org/junit4/javadoc/4.12/org/junit/Rule.html)を用いることで対応します。

実行対象のテストクラスに以下のようなコードを追加します。

```Java
    @Rule
    public TestWatcher watchman = new TestWatcher() {

        @Override
        protected void succeeded(Description d) {
            System.out.println("I am succeeded() method. name -> " + d.getMethodName());
            screenRecorder.getCreatedMovieFiles().stream().filter(a -> a.toPath().toString().contains(testName.getMethodName())).forEach(a -> a.deleteOnExit());
        }

        @Override
        protected void failed(Throwable th, Description d) {
            System.out.println("I am failed() method. name -> " + d.getMethodName());
            System.out.println(th.toString());
        }
    };
```

Overrideしている`succeeded`および`failed`で成功・失敗時処理を記述できます。
ここでは、成功時は録画ファイルの名前を取得し削除処理を、失敗時には標準出力へのログ出力のみを行っています。

またここでは録画ファイル名にはテストメソッド名を基準しているのですが、実行時のテストメソッド名を取得するために以下のような記述もします。

```Java
    @Rule
    public TestName testName = new TestName();
```

## GitHub 

[前回](https://qiita.com/shimashima35/items/411f99a27b7ec5503532)同様、[Codezineの記事](https://codezine.jp/article/detail/10335)のサンプルソースをベースに追記しました。前回分のWebDriverManager利用も含まれています。

https://github.com/shimashima35/codezine-sample/tree/feature/screen-recording

## 再生方法

さて最後に再生方法です。
今回の使用したライブラリでは複数種類のコーデックが選べるようですが、ファイルサイズの関係でTechSmith Screen Capture Codecというものを使用しています。商用ソフトで利用されているコーデックのようですが、幸い[デコーダはダウンロード可能](https://www.techsmith.com/codecs.html)なのでダウンロードしてコーデックをインストールすることで再生が可能です。

なおパラメータや指定方法は忘れてしまいましたが、筆者は他のエンコーディング方式も試してみましたがファイルサイズが極端に大きくなって(数百MByte)しまい実用に耐えないので諦めました。

今回利用したMonte Media Library以外の録画ライブラリを用いることでこのあたりの制約は緩和可能かと思います。

# おわりに

簡単ですが録画機能実装について説明しました。いかがだったでしょうか。
比較的面倒くさいと思われている録画も、ライブラリを使えば意外に簡単に実装できたりします。
皆様のSeleniumライフが楽しいものになることを陰ながら応援しています。