Katalon StudioでVisual Testingを実現した

# はじめに

わたしはWebアプリケーションのUIをテストとする作業をソフトウェアで自動化する技術に関心があります。2018年1月以来わたしは [Katalon Studio](https://www.katalon.com/) というソフトウェアに注目しています。[「Katalon Studioとはどんなソフトウエアか」](https://qiita.com/kazurayam/items/aece5d314f84893a0a14)で概要を紹介しました。

Webシステムの本番環境の画面と開発環境の画面を見比べて大きな違いがあったらテストがfailする、あるいは本番環境の入れ替え作業の前と後の画面を見比べて大きな違いがあったらテストがfailする、そういう自動化テストをKatalon Studioで実現しました。この自動化テストを本記事で紹介します。

以下の文章はGitHubレポジトリ [VisualTestingInKatalonStudio](https://github.com/kazurayam/VisualTestingInKatalonStudio) のREADMEとほぼ同じ内容を日本語で書き直したものです。


# 解決すべき問題

Katalon Studioで *Visual Testing* を実現したい。Visual Testingとは何か？画面確認の作業を自動化することです。

下記のようなことをわたしは実現したいとおもっていて、それを実現する手法のことをVisual Testingと呼ぶことにしました。

1. わたしはブラウザで某Webサイトを見に行く。しょっちゅう見に行く。わたしはそのWebの開発者かもしれない、デザイナかもしれない、テスト担当者かもしれないし、ただのファンかもしれない。
2. そのWebサイトのページをできるだけ多く目で見て確認したい。システムになんらか不具合が発生した時どこに顕在化するか予測できない。だからひとつふたつページを選んで見るのでは駄目。広く目配りすることが重要。
3. Webページ全体のスクリーンショットを撮ってファイルに保存したい。証拠(evidence)として残したい。
4. Webサイトの本番環境と開発環境と２つの環境の画面を目で見比べるように、2つの環境のスクリーンショット画像に差異があるかどうかをチェックしたい。
5. Webサイトの本番環境で入れ替え作業前に画面のスクリーンショットをとっておき、作業後にもう一度とって、前後を見比べたい。
6. 画面の食い違いの大きさを数値化したい
7. 一連の作業を自動実行したい

Webサイトに100ページもあったらスクリーンショットを撮るのにどれぐらい時間がかかるでしょうか。人手でやったら1時間以上かかるだろう。しんどくて退屈な作業だ。そんな奴隷労働なんかやりたくない。でも会社からやれといわれてやらざるをえない場合がある。そんな時のためにツールを開発して画面確認作業を自動化しよう。

わたしはVisual Testingツールが2つの環境の画面を見比べて重大な差異があればそれに気づかせてくれることだけを望んでいます。Visual Testingツールに本格的なデバッグ機能を求めない。ただし無償で利用可能であってほしい。

Katalon Studioでテスト・スクリプトを書き始めてすぐ以下の課題に気づきました。

## (1) 画像ファイルを整理整頓するのが面倒だという課題

わたしは本番環境と開発環境と2つの環境のスクリーンショットをたくさん撮ってローカルディスクにいったん保存し、あとで再利用したいと考えた。あるスクリプトがファイルのPathを決めてWRITEする。べつのスクリプトがファイルのPathを発見してREADする。そうするためにはスクリーンショットのファイルのPathをどうするか仕様を決めて2つのスクリプトがその仕様に基づいて動作するようにしなければならない。技術的に高度ではないが面倒くさいコードを書く必要がありました。

## (2) ページ全体のスクリーンショットが撮れないという課題

Selenium WebDriverのAPIに [`org.openqa.selenium.TakesScreenshot#getScreenshotAs(output)`](https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/TakesScreenshot.html)がある。これを使えばWebページのスクリーンショットを撮ることができる。しかし実際にやってみると目に見えている部分（View Port）だけが撮影されるが、画面全体のスクリーンショットを取ることができない。これでは不十分だ。

## (3) ふたつの画像を比較したいという課題

開発環境のWebページのスクリーンショットをとり、本番環境のスクリーンショットをとってPNG画像ファイルにすることはできたが、それだけでは不十分だ。二つのPNG画像を見比べてどの部分が食い違っているのかがすぐわかるようにしたい。人が２つの画像を見比べて些細な食い違いを見いだすのは甚だ困難だから。また食い違い部分の面積と画面全体の面積の比率 **diff ratio** を数値として求めたい。diff ratioが0.00なら２つの画像は完全に同じ、0.12ならちょっと違うところがある、70.45なら甚だ大きな違いがある、といえる。画面の見た目の違いをdiff ratioという数値で語れるようにしたい。


# 解決方法

## (1) ファイルのPathにかんする課題の解決

この問題を解決するためにわたしは下記のGroovyライブラリを開発しました。

- [`Materials`](https://github.com/kazurayam/Materials)

Materials-X.X.X.jarをKatalon Studioに組み込んで使います。Katalon Studioのプロジェクトのディレクトリに `Materials` ディレクトリを作り、その下に整理整頓されたファイル・ツリーを構築します。そしてファイルにアクセスするためのメソッドを提供します。

Materials-X.X.X.jarの使い方を紹介するのが下記：

- [UsingMaterialsInKatalonStudio](https://github.com/kazurayam/UsingMaterialsInKatalonStudio) --- サンプルとして動くKatalon Project

UsingMaterialsInKatalonStudioプロジェクトはあるWebサイトの2つの環境にアクセスしてスクリーンショットをたくさん撮りPNGファイルにしてローカルディスクに保存したうえでそれらを一覧するHTMLを生成します。REAMDE（英文）を日本語で書き直したのが下記です:

- [Katalon Studio+MaterialsでWebサイトのスクリーンショットを整然と保存する](https://qiita.com/kazurayam/items/6d76148fa866129696b7)


## (2) ページ全体のスクリーンショットの解決

この問題を[aShot](https://github.com/yandex-qatools/ashot) が解決してくれました。aShotをKatalon Studioで使ってみようとデモを作成し下記のGitHubレポジトリにアップしまし。

- [Entire page screenshot by aShot in Katalon Studio](https://github.com/kazurayam/EntirePageScreenshotByAShotInKatalonStudio)

## (3) ふたつの画像ファイル比較の解決

aShot の [`ImageDiff`](https://github.com/pazone/ashot/blob/master/src/main/java/pazone/ashot/comparison/ImageDiff.java) が解決してくれました。

aShotをKatalon Studioのなかで使得るよう、わたしはもうひとつKatalon Studioプロジェクトを作ってGitHubで公開しました。

- [`ksbackyard`](https://github.com/kazurayam/ksbackyard)

このプロジェクトのなかにはカスタム・キーワード `com.kazurayam.ksbackyard.ScreenshotDriver` が含まれています。Materialsディレクトリのなかから比較すべきファイルのリストを取り出したり、2つの画像ファイルの隔たりの大きさを示す数値を算出するなどのメソッドを備えています。

これらの外部リソースを統合して [`VisualTestingInKatalonStudio`](https://github.com/kazurayam/VisualTestingInKatalonStudio) プロジェクトを仕上げました。これは冒頭わたしが「Visual Testingとはこういうもの」と述べた要件を満たすものになっています。

# ツールが出力したレポートのサンプル

某日、[VisualTestingInKatalonStudioプロジェクトのTest Suites/CURA/Execute_twins](https://github.com/kazurayam/VisualTestingInKatalonStudio/blob/master/Test%20Suites/CURA/Execute_twins.ts)を実行して作成されたindex.htmlをネットにアップロードしたサンプルがあります。こんな結果レポートが最終的に得られます。
- http://vtprojects.kazurayam.com/VisualTestingInKatalonStudio/Materials/index.html

# VisualTestingInKatalonStudioのサンプルコードを動かす方法

## ダウンロードサイト

[Releases](https://github.com/kazurayam/VisualTestingInKatalonStudio/releases) ページにZIPファイルが公開されています。これをダウンロードして解凍してください。

## 前提するKatalon Studioのバージョン

2020年1月現在で最新バージョンであるKatalon Studio ver7.2.1を使うことを前提して以下説明を記述します。ただしVisualTestingInKaztalonStudioプロジェクトはもともとKatalon Studio ver5.6で開発されました。その後のバージョンアップにも影響されないことを検証済みです。

## TwinsモードとChronosモード

[VisualTestingInKatalonStudio](https://github.com/kazurayam/VisualTestingInKatalonStudio)プロジェクトは2018年秋に最初のバージョンがリリースされました。初期バージョンでは、あるWebシステムの本番環境と開発環境（つまり2つ別々のURL）を大体同じタイミングでスクリーンショットして比較するという方法を実装しました。これを **Twinsモード** と名付けました。Twinsとは「双子 ふたご」のことです。テスト対象のWebシステムがすでに安定的に本番稼働している一方で開発環境で開発作業を進めている時に、開発環境のWebページが本番環境と比べてどう変化しているかを目視する作業 --- 開発者は毎日何度もこれを繰り返すでしょう --- を省力化するのに役立ちます。あるいは共通モジュールやDBのデータを変更した時それが画面上に及ぼす影響を広く目視確認しようという場合に役立ちます。

2019年春にもう一つの機能を追加しました。あるWebシステムの1つのURLのスクリーンショットを時間的に前後させて都合２回取得して出来た画像群を比較する。これを **Chronosモード** と名付けました。Chronosとは「時間」のこと。Webサービスの本番環境でソフトウェアの入れ替えや環境設定の修正などの作業を行う場合に、作業前と作業後の２度にタイミングを分けてスクリーンショットを取得し、できたスクリーンショット画像を比較する作業を自動化することができます。スクリーンショットを100個取得するようなテストコードを事前に準備しておいて作業当日に実行しましょう。あなたがコーヒーを飲んでいる10分ほどの間にKatalon Studioがスクリーンショット採取と画像比較をしてくれます。最後に生成されたindexページをみれば本番環境での作業が満足できるものであったかどうか証跡に基づいて判定することができるでしょう。

## Twinsモードのサンプルを実行する手順

1. [Katalon Studio](https://www.katalon.com/)を起動し、`VisualTestingInKatalonStudio`プロジェクトを開く
2. Tests ExplorerペインのなかにTest Suitesアイコンがあるのを選びます。その下にサブフォルダ`CURA`があります。さらにその下に`CURA/Execute_twins`がある。このアイコンをダブルクリックして開く。`CURA/Execute_twins`はテストスイートコレクションです。`CURA`はテスト対象であるWebサイトのシンボルです。
3. `CURA/Execute_twins`はFirefoxブラウザがあなたのPCにインストール済みで使用可能であることを暗黙に前提している。もしなかったら[Firefoxをインストール](https://support.mozilla.org/ja/products/firefox/install-and-update-firefox)してください。
4. テストスイートコレクション `CURA/Execute_twins` の定義画面の中に緑色の矢印つきで `Execute` というボタンがあるのでそれを押して実行します。![スクリーンショット 2020-01-08 11.24.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/ea6f7219-57b9-b314-8c07-47a40fafaefe.png)
5. テストが走り終わるまでに2分ぐらいかかります。
6. `VisualTestingInKatalonStudio/Materials/index.html`というファイルができます。これをWebブラウザで開いて見てください。

Katalon Studioの古いバージョンでは `<projectDir>/Materials` ディレクトリとそのなかのファイルをKatalon StudioのGUIのなかで見ることができませんでした。しかし2019年5月にリリースされた[ver6.1.5以降なら可能](https://forum.katalon.com/t/want-to-edit-gitignore-and-readme-md-in-katalon-studio/12268/10)です。Katalon StudioのGUIツールバーで Project > Reflesh を実行してプロジェクトを再読み込みしてください。`<projectDir>/Materials/index.html`をダブルクリックすればWebブラウザが起動されてindex.htmlを見ることができます。

### デモ `CURA/Execute_twins` の説明

テストスイートコレクション `CURA/Execute_twins` が何をするのか、概要を説明します。

#### 入力

テストスイートコレクション `CURA/Execute_twins` はテストスイート `Test Suites/CURA/twins_capture` を二度実行します。最初は本番環境に向けて。二度目は開発環境に向けて。下記ふたつのURLにアクセスします。

- http://demoaut.katalon.com/ --- Production すなわち本番環境
- http://demoaut-mimic.kazurayam.com/ --- Development すなわち開発環境

#### 処理内容

テストスイート `Test Suites/CURA/twins_capture` は指定されたURLを訪問し、順番にページをたどってスクリーンショットを撮ります。`./Materials/CURA.twins_capture/yyyyMMdd_hhmmss/CURA.visitSite` というディレクトリが作られて中にPNGファイルが5つ保存されます。

続いてテストスイートコレクション `CURA/Execute_twins` はテストスイート `CURA/twins_exam` を実行します。テストスイート `CURA/twins_exam` はPNGファイル群を読み同じ画面どおし比較して、差分画像を生成します。生成した差分画像を `./Materials/CURA.twins_exam/yyyyMMdd_hhmmss/CURA.ImageDiff_twins/CURA.visiteSite` というディレクトリに保存します。

比較された2つの画面に差異があれば差分画像のなかに下記のような赤く塗られた領域ができます。ひと目見れば「あれ、なんか違っているぞ！」とわかりますね。
<img src="https://qiita-image-store.s3.amazonaws.com/0/52995/f9cf4a42-780e-ae24-b874-ea398e338dd8.png" width=50%">



下記がシーケンス図です。

![sequence-diagram.png](https://qiita-image-store.s3.amazonaws.com/0/52995/8e62a333-5a54-bf6d-850c-dc6648bd44a6.png)


#### 差分画像のファイル名の形式

差分画像ファイルのファイル名は下記のようなフォーマットに従います。例をあげますと:
`home(48.71)FAILED.png`

ファイル名の最初の部分 `home` は比較の元になったファイル名 `home.png` からコピった名前です。

差分ファイルのファイル名の末尾にある `(48.71)` をわたしは diff ratioないしdiff% と呼びますが、定義は
`diff % := number of RED pixcels(differences) / (width * hight of page) * 100` です。つまり画面全体の広さを100としたときに赤く塗られた差分領域の広さがどれだけあるかを示すパーセンテージです。本番環境と開発環境とが完璧に一致していれば diff% は 0.00 ですが、diff%が0より大きい値になることがしばしばです。

差分ファイルのファイル名の末尾に `FAILED` という文字があったり無かったりします。`FAILED`のマークの有無がどうやって決まるか？それを理解するには `Test Cases/CURA/ImageDiff_twins` のコードを見る必要があります。すなわち：

```
import com.kazurayam.visualtesting.ImageDiffer

String TESTSUITE_ID = 'cURA/twins_capture'
double criteriaPercentage = 1.0
ImageDiffer imageDiffer = new ImageDiffer()
boolean result = imageDiffer.runTwins(TESTSUITE_ID, criteriaPercentage)
```

runTwins()メソッド呼び出しの第2引数に `1.00` という数値が指定されています。これをわたしは *criteria%* と呼びます。criteriaとは「目安」のこと。差分画像のdiff%が48.71ならばcriteria%の1.00より大きい、だからこの差分は重要であると判断されて、`FAILED` とマークがつけられます。`FAILED`したら要注意です。

#### 出力

某日作成されたindex.htmlをネットにアップロードしたサンプルがあります。
- http://vtprojects.kazurayam.com/VisualTestingInKatalonStudio/Materials/index.html

インデックス画面 index.html がこれ:
![スクリーンショット 2020-01-08 11.59.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/095367d0-261b-f213-5461-4791c5bd6604.png)

`FAILED`とマークされた差分画像ファイルの<font color="MediumPurple">バーの背景色が紫色</font>で強調表示されることに注意。ただしdiff%の数値が0.12とかでcriteria%の数値（たとえば1.00）より小さければその差分は重要でないとみなされFAILEDマークがつきません。だからindex画面上も強調表示されません。

強調表示された `home(48.71)FAILED.png` をクリックするとダイアログが開き、その中に差分画像が表示されます。赤い領域が大きいのにちょっと驚きます。
![スクリーンショット 2020-01-08 12.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/3141971f-471c-347f-172b-90916925a6f8.png)

ダイアログの左辺ないし右辺をクリックすると画像がスライドして下記のような表示に切り替わります。
![スクリーンショット 2020-01-25 12.23.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/ed724fe7-27a1-cfae-b664-07bce1146d47.png)

左に本番環境の画面のスクリーンショット、右に開発環境の画面のスクリーンショットが並べて表示されます。二つをチラチラ見比べるとどこがどう違っているから赤く表示されたのか理由がわかります。

## Chronosモードのサンプルを実行する手順

1. [Katalon Studio](https://www.katalon.com/)を起動し、`VisualTestingInKatalonStudio`プロジェクトを開く
2. Tests ExplorerペインのなかにTest Suitesアイコンがあるのを選びます。その下にサブフォルダ`CURA`があります。さらにその下に`CURA/Execute_chronos`がある。このアイコンをダブルクリックして開く。
3. `CURA/Execute_chronos`はFirefoxブラウザがあなたのPCにインストール済みで使用可能であることを暗黙に前提している。もしなかったら[Firefoxをインストール](https://support.mozilla.org/ja/products/firefox/install-and-update-firefox)してください。
4. テストスイートコレクション `CURA/Execute_chronos` の定義画面の中に緑色の矢印つきで `Execute` というボタンがあるのでそれを押して実行します。
5. テストが走り終わるまでに1分ぐらいかかります。
6. `CURA/Excecute_chronos`を初めて実行した時には（あまり褒められた仕様ではない --- 実装の手落ち --- のですが）GroovyコードがExceptionを投げてエラー終了します。これは理由があってのこと。気を撮り直してもう一度 `CURA/Excecute_chronos` を実行してください。 
7. `VisualTestingInKatalonStudio/Materials/index.html`というファイルができます。これをWebブラウザで開いて見てください。

### デモ `CURA/Execute_chronos` の説明

テストスイートコレクション `CURA/Execute_chronos` がどのように動作するか、下記に説明します。 

#### 入力

テストスイートコレクションは `CURA/Execute_chronos` 下記のURL にアクセスしてスクリーンショットを取得します。

- http://demoaut-mimic.kazurayam.com

`CURA/Execute_chronos` は今回取得したスクリーンショット画像を `<projectDir>/Storage` ディレクトリへ保管します。こんな風に。

![スクリーンショット 2020-01-08 13.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/fb8a3098-b06b-991e-0bf6-15e41cf92edb.png)

`CURA/Execute_chronos`は実行されると `<projectDir>/Storage`ディレクトリに保管された過去
のスクリーンショット画像の中から適切なものを選択します。選択された過去のスクリーンショット画像を `<projectDir>/Materials`フォルダにコピーします。これによりMaterialsフォルダの下に「前の画像」と「今の画像」が揃います。二つの画像群を比較して差分をとりレポートを出力するしくみは `CURA/Execute_twins` と同じです。

#### 処理内容

`CURA/Execute_chronos` を2回実行します。本番環境でする作業の前に一度。そして作業完了後にもう一度。そうすることで前後の比較が可能になります。

`CURA/Execute_chronos`が`<projectDir>/Storage`のなかから比較対象となる「過去の画像」を選択する時、どういう基準で選ぶのでしょうか？それは

- [Test Cases/CURA/restorePreviousScreenshots](https://github.com/kazurayam/VisualTestingInKatalonStudio/blob/master/Scripts/CURA/restorePreviousScreenshots/Script1560821220922.groovy)

に記述しています。例えば

```
WebUI.callTestCase(findTestCase("Test Cases/VT/restorePreviousTSuiteResult"),
    ["STRATEGY":"last"])
```
STRATEGY:lastがデフォルトですが、lastすなわち「前回」です。今回の一回前に `CURA/Execute_chronos` が実行された時に保管されたスクリーンショット画像群が選択されます。今回が何月何日何時何分何秒であってもそれは関係なくて、今回より一つ前です。

他にも選択の仕方があります。

```
WebUI.callTestCase(findTestCase("Test Cases/VT/restorePreviousTSuiteResult"),
    ["STRATEGY":"exactlyAtOrBefore", "timestamp":"20191205_110904" ])
```

STRATEGY:exactlyAtOrBeforeはtimestampとして指定された年月日時分秒に一致する記録が有ればそれを、無ければそれより前の一番近いやつを選びます。

他にどんなSTRATEGYが選択可能なのか？
[Test Cases/VT/restorePreviousTSuiteResult](https://github.com/kazurayam/VisualTestingInKatalonStudio/blob/master/Scripts/VT/restorePreviousTSuiteResult/Script1550220558541.groovy)のソースをみてください。


#### 出力

テストスイートコレクション `CURA/Execute_chronos` は`CURA/Execute_twins`とまったく同じく `<projectDir>/Materials/index.html` を出力します。

## 外部依存性について

### 外部jarへの依存性

`VisualTestingInKatalonStudio`プロジェクトは下記のjarファイルに依存しています。

- ['Materials-x.x.x.jar'](https://github.com/kazurayam/Materials/releases)
- ['ashot-1.5.4.jar'](https://mvnrepository.com/artifact/ru.yandex.qatools.ashot/ashot/1.5.4)
 
2つのjarが [`<projectDir>/Drivers/`](https://github.com/kazurayam/VisualTestingInKatalonStudio/tree/master/Drivers) のなかにあらかじめ組み込んであります。

外部のjarをKatalon Studioに組み込む方法がドキュメント ["External Libraries"](https://docs.katalon.com/display/KD/External+Libraries) で説明されています。この操作をしてMaterialsやaShotをアップグレードすることができます。

### カスタム・キーワードの依存性

`VisualTestingInKatalonStudio`プロジェクトは下記の外部プロジェクトで開発されたCustom Keywordのソースコードをimportして再利用しています。

- ['ksbackyard'](https://github.com/kazurayam/ksbackyard/releases)

ksbackyardプロジェクトの [Releases](https://github.com/kazurayam/ksbackyard/releases) ページから `ksbackyard-all.jar` ファイルをダウンロードすることができます。このjarをKatalonプロジェクトのDriversディレクトリに放りこむことで参照可能になります。



# あなたのWebサイトにVisual Testingを応用するにはどうすればいいか

[VisualTestingInKatalonStudio](https://github.com/kazurayam/VisualTestingInKatalonStudio)のサンプルは `http://demoaut.katalon.com` というURLを対象としています。しかしあなたがテストしたいURLはこれではないですよね。*あなたがテストしたいURLに対してVisual Testingの方法を適用するにはどうすればいいのでしょうか？*　ーーー　それを可能にする方法を開発しました。別記事「[あなたのWebサイトをVisual Testingする方法](https://qiita.com/kazurayam/items/7da4bd82b52fe18209db)」で説明します。