あなたのWebサイトをVisual Testingする方法

# はじめに

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

2018年秋にわたしは [VisualTestingInKatalonStudio](https://github.com/kazurayam/VisualTestingInKatalonStudio) を開発し公開しました。これはKatalon Studioを使って作ったプロジェクトです。このプロジェクトは http://demoaut.katalon.com/ というURLをターゲットとしてスクリーンショットを撮り、画像ファイルを比較することで画面確認作業を自動化するというものでした。

# 解決すべき問題

しかしあなたがテストしたいURLは http://demoaut.katalon.com/ ではない。URLを特定するのではなく、あなたが関心をよせている任意のWebサイトに対してVisual Testingの手法を応用することを可能としたい。できるか？

# 解決方法

1. あなたは自分のPCに [Katalon Studio](https://www.katalon.com/) をインストールして使えるようにします。
2. あなたはKatalon Studioのプロジェクトを一つ新規に作成します。
3. [VisualTestingInKatalonStudio](https://github.com/kazurayam/VisualTestingInKatalonStudio)プロジェクトが実装したスクリプト一式とサンプルコードをネットからダウンロードして、あなたが新規に作ったプロジェクトの一部として取り込んでしまいます。この処理はビルドツール Gradle を用いた仕組みによってほぼ自動的に行われます。
4. 取り込まれたサンプルを参考にしながら、コードをコピーしフォルダ名やファイル名を変更して、内容を若干書きかえることにより、あなたのAUT(Application Under Test)のためのVisual Testingの仕組みを構築することができます。
5. 最後に、あなたは自分が画面確認したいWebサイトをvisitしてスクリーンショットを取るTest Caseスクリプトを開発します。これはあなたの仕事です。

# 説明

あなたがgitの操作に習熟していると前提してgitコマンドに関する説明は省略させてもらいます。

## どのWebサイトをターゲットとするか

インターネットで一般に公開されている下記のサイトをターゲットにスクリーンショット画像を取得すること、そして少し前に取得した画像と今取得した画像を見比べること、すなわちVisual TestingをKatalon Studioで実装することをサンプルとして提示します。

- 47ニュース (https://www.47news.jp/) 共同通信社

下記のGitHubレポジトリにわたしが作ったサンプルプロジェクトを公開しました。

- https://github.com/kazurayam/HowToEnableVisualTesting

なぜこのサイトを選んだかというと、このサイトが写真を中心とするコンテンツを高頻度で更新するからです。日本時間の日中なら１時間後にはきっと画面のどこかが変化します。だからツールで今の画面と１時間前の画面とを比較すればきっと食い違いを発見することができる。Visual Testingの効果を見るのにとても好都合です。

あなたがどのWebサイトをターゲットとしてVisual Testingを構築するか、それは当然ながらあなた次第です。自分がやりたいターゲットを選んでください。どのURLをターゲットとするのでも、Visual Testingの仕組みを適用する手順は同じです。

## (1) Katalon Studioをインストールする

https://docs.katalon.com/katalon-studio/docs/getting-started.html を参照のこと

## (2) Katalonプロジェクトを新規に作る

Katalon Studioのウインドウのツールバーで `File > New > Project` する。プロジェクト名はもちろんあなたの好きなように。私は今回サンプルとして `HowToEnableVisualTesting` というプロジェクトを作りました。

=>[tags:step0](https://github.com/kazurayam/HowToEnableVisualTesting/tree/step0)

## (3) ビルドツール Gradleをインストールする

- [本家](https://gradle.org/install/)
- [Qiita: Windows10にGradleをインストールする](https://qiita.com/quwahara/items/9c4e5fbda421cfcb09ad)

コマンドラインで下記のことができればOKです。Gradleの基本的な機能だけを使いますからバージョンにはさほどこだわりません。

```
$ gradle --version

------------------------------------------------------------
Gradle 5.4
------------------------------------------------------------

Build time:   2019-04-16 02:44:16 UTC
Revision:     a4f3f91a30d4e36d82cc7592c4a0726df52aba0d

Kotlin:       1.3.21
Groovy:       2.5.4
Ant:          Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM:          1.8.0_221 (Oracle Corporation 25.221-b11)
OS:           Mac OS X 10.13.6 x86_64
```

もしもあなたのPCがProxyの中にいてgradleがインターネット上のリソースにアクセスするのにProxyを通過しなければならない場合には、GradleにProxyを設定する必要があります。ホームディレクトリの下に `.gradle/gradle.properties` ファイルを作って下記のように記述します。

```
systemProp.http.proxyHost=172.24.xxx.xxx
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=172.24.xxx.xxx
systemProp.https.proxyPort=8080
```


## (4) build.gradle ファイルを作る

テキストエディタで `<projectDir>/build.gradle` ファイルを作ります。内容は下記の通り。

```
plugins {
  id "com.github.kazurayam.visualtestinginks" version "0.1.21"
}
vt.version = '1.17.3'
```

`com.github.kazurayam.visualtestingins`はわたしが開発したGradleプラグインです。[VisualTestingInKatalonStudio](https://github.com/kazurayam/VisualTestingInKatalonStudio)プロジェクトの成果物をカレントディレクトリであるKatalonプロジェクトにimportする処理を実装しています。

`vt.version` は [VisualTestingInKatalonStudioプロジェクトのReleasesページ](https://github.com/kazurayam/VisualTestingInKatalonStudio/releases) で公開されている配布物のバージョン番号を指定するものです。バグの対策や仕様変更を反映するためにvt.versionはときどき更新されます。


関連情報: [visualtestinginksのuserguide](https://github.com/kazurayam/visualtestinginks-gradle-plugin/blob/master/docs/userguide.md)

なおKatalon Studioのver7.0以降はプロジェクトを新規作成した時にbuild.gradleファイルを自動的に作成します。自動生成された内容はVisualTestingするのに不要ですから削除して、上記の４行で置き換えてください。


## (5) Visual Testingのリソースを取り込む

コマンドラインで下記の操作をします。

```
$ cd <あなたのKatalonプロジェクトのディレクトリ>
$ gradle enableVisualTesting
```

するとたくさんのメッセージが表示されます。インターネットからファイルがダウンロードされてKatalonプロジェクトの中に取り込まれます。

```
:HowToEnableVisualTesting [master]$ gradle enableVisualTesting
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :importVTComponents
downloading sourceURL=https://github.com/kazurayam/VisualTestingInKatalonStudio/releases/download/1.13.0/vt-components-1.13.0.zip into /var/folders/2j/_1868sm54yddf8_9xf0k3qrw0000gn/T/ImportVTComponentsTask7547224480483831820/vt-components-1.13.0.zip
System.getProperty('http.proxyHost')=null
System.getProperty('http.proxyPort')=null
System.getProperty('https.proxyHost')=null
System.getProperty('https.proxyPort')=null

Download https://github.com/kazurayam/VisualTestingInKatalonStudio/releases/download/1.13.0/vt-components-1.13.0.zip
Download https://github.com/kazurayam/VisualTestingInKatalonStudio/releases/download/1.13.0/vt-example-1.13.0.zip

> Task :importVTExample
downloading sourceURL=https://github.com/kazurayam/VisualTestingInKatalonStudio/releases/download/1.13.0/vt-example-1.13.0.zip into /var/folders/2j/_1868sm54yddf8_9xf0k3qrw0000gn/T/ImportVTExampleTask1511888025211013748/vt-example-1.13.0.zip
System.getProperty('http.proxyHost')=null
System.getProperty('http.proxyPort')=null
System.getProperty('https.proxyHost')=null
System.getProperty('https.proxyPort')=null

BUILD SUCCESSFUL in 31s
2 actionable tasks: 2 executed
```

インポートの処理が終わったところでプロジェクトディレクトリには下記のようなフォルダ・ファイルが追加されます。

```
.
├── Drivers
│   ├── VT-Materials-0.73.2.jar
│   ├── VT-ashot-1.5.4.jar
│   ├── VT-commons-lang3-3.6.jar
│   ├── VT-commons-math3-3.6.1.jar
│   ├── VT-espionagekeyword-0.1.0.jar
│   ├── VT-junit4ks-all.jar
│   ├── VT-ksbackyard.jar
│   └── VT-thumbnailator-0.4.8.jar
├── Keywords
│   └── com
│       └── kazurayam
│           └── visualtesting
│               ├── DOMRect.groovy
│               ├── Downloader.groovy
│               ├── GlobalVariableHelpers.groovy
│               ├── Helpers.groovy
│               ├── HighlightElements.groovy
│               ├── ImageCollectionDifferDriver.groovy
│               ├── ImageDiffer.groovy
│               ├── ImageDiffsLister.groovy
│               ├── ManagedGlobalVariable.groovy
│               ├── VTConfig.groovy
│               ├── VisualTestingListenerImpl.groovy
│               └── VisualTestingLoggerImpl.groovy
├── Object\ Repository
│   └── CURA
│       ├── Page_Appointment
│       │   ├── button_Book\ Appointment.rs
│       │   ├── h2_Make\ Appointment.rs
│       │   ├── input_hospital_readmission.rs
│       │   ├── input_programs.rs
│       │   ├── input_visit_date.rs
│       │   ├── select_Tokyo\ CURA\ Healthcare\ C.rs
│       │   └── textarea_comment.rs
│       ├── Page_AppointmentConfirmation
│       │   ├── a_Go\ to\ Homepage.rs
│       │   ├── p_comment.rs
│       │   ├── p_facility.rs
│       │   ├── p_hospital_readmission.rs
│       │   ├── p_program.rs
│       │   └── p_visit_date.rs
│       ├── Page_Homepage
│       │   └── a_Make\ Appointment.rs
│       └── Page_Login
│           ├── button_Login.rs
│           ├── input_password.rs
│           └── input_username.rs
├── Profiles
│   ├── CURA_DevelopmentEnv.glbl
│   ├── CURA_ProductionEnv.glbl
│   └── 
├── Test\ Cases
│   ├── CURA
│   │   ├── ImageDiff_chronos.tc
│   │   ├── ImageDiff_twins.tc
│   │   ├── Login.tc
│   │   ├── restorePreviousScreenshots.tc
│   │   ├── test
│   │   │   ├── LoginTest.tc
│   │   │   └── TC1.tc
│   │   └── visitSite.tc
│   └── VT
│       ├── backupCurrentTSuiteResult.tc
│       ├── cleanMaterials.tc
│       ├── makeIndex.tc
│       ├── reduceStorageSize.tc
│       ├── restorePreviousTSuiteResult.tc
│       └── utils
│           ├── backupAll.tc
│           ├── emptyStorage.tc
│           ├── executeSystemCommandExample.tc
│           ├── listRepository.tc
│           ├── listStorage.tc
│           └── skelton.tc
├── Test\ Listeners
│   └── VTListener.groovy
├── Test\ Suites
│   ├── CURA
│   │   ├── Execute_chronos.ts
│   │   ├── Execute_chronos_headless.ts
│   │   ├── Execute_twins.ts
│   │   ├── Execute_twins_headless.ts
│   │   ├── chronos_capture.groovy
│   │   ├── chronos_capture.ts
│   │   ├── chronos_exam.groovy
│   │   ├── chronos_exam.ts
│   │   ├── twins_capture.groovy
│   │   ├── twins_capture.ts
│   │   ├── twins_exam.groovy
│   │   └── twins_exam.ts
│   └── VT
│       ├── cleanMaterials.groovy
│       ├── cleanMaterials.ts
│       ├── makeIndex.groovy
│       └── makeIndex.ts

```

結構たくさんありますが、あなたが注意を払うべきファイルは少ないです。どのファイルに注意すべきか、詳しくはあとで説明します。

上記は vt.version=1.13.0 の場合の例です。vt.versionが違えば追加されるフォルダ・ファイルが違っている可能性があります。

=>[tags:step2](https://github.com/kazurayam/HowToEnableVisualTesting/tree/step2)

## (6) KatalonプロジェクトをRefleshする

あなたが上記のgradleコマンドを実行する時にもしもKatalon Studioのウインドウを開いていた場合は一度Katalon Studioを閉じて再起動してください。gradleが取り込んだファイルやフォルダをKatalon Studioに認識させるためにこの操作が必要です。

## (7) 動作確認

Katalon Studioを起動し HowToEnableVisualTestingプロジェクトを開き、テストスイートコレクション `Test Suites/CURA/Execute_twins` を実行してみましょう。

![スクリーンショット 2020-01-08 16.51.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/82deee8f-06c5-4e2a-e385-1f5653c2c4cb.png)

まともに動いて `<projectDir>/Materials/index.html` が生成されるはずです。どうでしょうか?

## (8) `.gitignore`ファイルを訂正する

Katalon Studioのver7.0以降、プロジェクトを新規に生成した時に `<projectDir>/.gitignore` ファイルが自動生成されます。その内容は１行だけ:

```
/bin/
```

ところでVisualTestingInKatalonStudioのスクリプトは実行時にいくつかのフォルダを作成しその中にサイズの大きなスクリーンショット画像ファイルをたくさん保存します。こういったファイルをGitレポジトリに入れてしまうのは良くない。だから `.gitignore` ファイルを修正しましょう。下記のようにしてください。

```
/bin/

/Libs/Temp*
/Reports/

/Drivers/
/Materials/
/Storage/
```

=>[tags:step3](https://github.com/kazurayam/HowToEnableVisualTesting/tree/step3)


## (9) Webサイトを表す短いシンボルを決める

VisualTestingInKatalonStudioプロジェクトから取り込まれたリソースのなかに `CURA` というフォルダがいくつかあります。`Test Cases/CURA`、`Object Repository/CURA`、`Test Suites/CURA`がそうです。`CURA`とは ターゲットであるURL http://demoaut.katalon.com/ が CURA Healthcare Serivcesという医療機関（もちろん仮想的なものですが）の名前の一部です。つまりテストのターゲットとするWebサイトを識別するコード（たとえばCURA）をプロジェクトのフォルダ構造の上位に位置付けることをVisualTestingInKatalonStudioは提唱しているのです。こうすることにより一つのKatalonプロジェクトが複数のWebサイトをターゲットとする場合にもコードが整理された状態を保つことができるからです。


![スクリーンショット 2020-01-08 17.00.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/dac62692-2b6b-d835-f82d-ec8eb374a657.png)

だからあなたが新しいKatalonプロジェクトで自分の関心あるWebサイトを対象としてVisual Testingを構築するにあたっては、まず　あなたのApplication Under Testを表す短いシンボルを決める必要があります。あなたの好きに決めてください。

わたしは今回 https://www.47news.jp/ をターゲットとしてサンプルを作るにあたっては

- `47news`

をシンボルとすることにしました。URLの一部を拝借しました。

## (10) Test Cases/XXXXを作る

ここからはあなたのターゲットとするURLをVisual Testingするための仕組みを作っていきます。

まずフォルダ `Test Cases/XXXX` を作ります。ここで `XXXX` はあなたが自分のAUTのために決めたシンボルに置換して解釈してください。フォルダの下にファイルを作ります。


|コピー元ファイル|新しく作るファイル|コードをどう修正するか|
|:---|:---|:---|
|Test Cases/CURA/ImageDiff_chronos|Test Cases/XXXX/ImageDiff_chronos|`CURA`の文字２箇所を`XXXX`に書きかえる。|
|Test Cases/CURA/ImageDiff_twins|Test Cases/XXXX/ImageDiff_twins|`CURA`の文字２箇所を`XXXX`に書きかえる|
|Test Cases/CURA/restorePreviousScreenshots|Test Cases/XXXX/restorePreviousScreenshots|`CURA`を`XXXX`に書きかえる|
|-|Test Cases/XXXX/visitSite|(11)で述べます|

Katalon StudioのGUIでTest Caseを一つ選択肢ダブルクリックするとデフォルトではManualビューという表形式の画面で開きます。Manualビューはプログラミングに不慣れな人向けのExcelライクなUIで、わたしはほとんど使いません。プログラマ向けのScriptビューがあります。こちらは普通のテキストエディタです。テキストエディタでスクリプトの中にある `CURA` という文字を `XXXX` に書きかえるという単純作業が最初にすべきことです。

## (11) Test Cases/XXXX/visitSiteの最初のバージョンを書く

`Test Cases/XXXX/visiteSite`はあなたがターゲットとするWebサイトをWebブラウザで開きスクリーンショットを撮ってPNG画像ファイルを保存するという処理を実現するスクリプトです。その最初のバージョンとして下記のコードをコピペしてください。ただしコードの中のURL文字列だけはあなたの意図するものに置き換えてください。

- [tags:step4 の Test Cases/47news/visitSite](https://github.com/kazurayam/HowToEnableVisualTesting/blob/step4/Scripts/47news/visitSite/Script1578471987018.groovy)

`Test Cases/XXXX/visitSite`がちゃんと動作することを確認しましょう。そのためにはKatalon studioのGUIで visitSite を開いた状態でRunボタン![run.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/729b4911-db5b-6dba-7df6-1526d21ab039.png)を押します。

`Test Cases/XXXX/visitSite`がちゃんと動作すれば `<projectDir>/Materials/_/_` フォルダの下にスクリーンショット画像ファイルが保存されるはずです。

![スクリーンショット 2020-01-08 18.14.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52995/60259e8c-cadc-ff84-88b9-9cf150408bc2.png)

もしも `Test Cases/XXXX/visiteSite` を実行してエラーが発生したらどうする？ --- エラーの要因は千差万別、がんばってデバッグしてください、と申し上げておきます。Katalon Studioの[ユーザフォーラム](https://forum.katalon.com/)に質問を（ただしin Englishで）あげればエキスパートがきっと助けてくれるでしょう。

## (12) Test Suites/XXXXを作る

次に `Test Suites`の下に新しいフォルダ `XXXX` を作ります。XXXXはあなたのAUTを表すシンボルに置換して解釈してください。フォルダ `Test Suites/XXXXの下に`

|コピー元ファイル|新しく作るファイル|どう修正するか|
|:---|:---|:---|
|Test Suites/CURA/chronos_capture|Test Suites/XXXX/chronos_capture|<ul><li>Test Cases/CURA/restorePreviousScreenshotsをTest Cases/XXXX/restorePreviousScreenshotsに置き換える</li><li>Test Cases/CURA/visitSiteをTest Cases/XXXX/visitSiteに置き換える</li></ul>|
|Test Suites/CURA/chronos_exam|Test Suites/XXXX/chronos_exam|Test Cases/CURA/ImageDiff_chronosをTest Cases/XXXX/ImageDiff_chronosに置き換える|
|Test Suites/CURA/Execute_chronos|Test Suites/XXXX/Execute_chronos|<ul><li>Test Suites/CURA/chronos_captureをTest Suites/XXXX/chronos_captureに置き換える</li><li>Test Suites/CURA/chronos_examをTest Suites/XXXX/chronos_exampに置き換える</li></ul>|
|Test Suites/CURA/twins_capture|Test Suites/XXXX/twins_capture|<ul><li>Test Cases/CURA/visitSiteをTest Cases/XXXX/visitSiteに置き換える</li></ul>|
|Test Suites/CURA/twins_exam|Test Suites/XXXX/twins_exam|<ul><li>Test Cases/CURA/ImageDiff_twinsをTest Cases/XXXX/ImageDiff_twinsに置き換える</li></ul>|
|Test Suites/CURA/Execute_twins|Test Suites/XXXX/Execute_twins|<ul><li>Test Suites/CURA/twins_captureをTest Suites/XXXX/twins_captureに置き換える</li><li>Test Suites/CURA/twins_examをTest Suites/XXXX/twins_examに置き換える</li></ul>|


## (13) Twinsモードをためしに動かす

`Test Suites/XXXX/Execute_twins` を開き Executeボタンを押して実行してみましょう。エラーにならず最後まで走るでしょうか？


## (14) Chronosモードをためしに動かす

`Test Suites/XXXX/Execute_chronos` を開き Executeボタンを押して実行してみましょう。

初めてXXXX/Execute_chronosを実行するときっと下記ののようなメッセージを出力してExceptionをthrowsします。
```
2020-01-08 20:39:14.491 ERROR c.k.katalon.core.main.TestCaseExecutor   - ❌ Test Cases/VT/restorePreviousTSuiteResult FAILED.
Reason:
com.kms.katalon.core.exception.StepFailedException: failed to retrieve a TSuiteResult of 
{
    "id": "Test Suites/47news/chronos_capture",
    "abbreviatedId": "47news/chronos_capture",
    "value": "47news.chronos_capture"
}
...
```
これは既知の振る舞いです。というのもChronosモードは「前回実行時に保存したスクリーンショット画像をStorageディレクトリから取り出して...」という動きをするからです。本当の1回目にはまだStorageが空っぽなので取り出すべき画像が見つからずエラーになってしまうのです。

もう一度 XXXX/Execute_chronosを実行してください。二回め以降はまともに動くはずです。

=>[tags:step5](https://github.com/kazurayam/HowToEnableVisualTesting/tree/step5)

あなたのApplication Under Test（つまりテスト対象のWebアプリ）をターゲットにVisual Testingを実践する準備は以上でほぼ整いました。

## (15) Test Cases/XXXX/visitSiteをちゃんと実装する

`Test Cases/XXXX/visitSite`はWebサイトをブラウザで開いてメニューボタンをクリックするなどしてサイトを巡回しながらスクリーンショットをとるスクリプトです。(11)でこのスクリプトの最小限の実装を行いました。つまりトップページのURLを開きスクリーンショットを1つ撮っておしまいにしました。

あなたのWebアプリのページをできるだけたくさんスクリーンショットしましょう。その処理を`Test Case/XXXX/visitSite`スクリプトに実装してください。それはあなたがすべき作業です。

[`Test Cases/CURA/visitSite`](https://github.com/kazurayam/HowToEnableVisualTesting/blob/step5/Scripts/CURA/visitSite/Script1554796633484.groovy)のコードを読み解いて参考にしてください。