Katalon Studioで始める、ちょっと本気のE2Eテスト 〜ハンズオンを添えて〜

本記事は[DMMグループ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/dmm)の24日目の記事です。

どうも、DMM.comの動画配信事業部というところでエンジニアやってます[@_tinoji](https://twitter.com/_tinoji)という者です。3年連続でクリスマスイブを担当させていただけて光栄です:christmas_tree:

クリスマスといえばE2Eテストですね。

今年、動画プレイヤーのちょっとしたE2Eテストを導入して、今も元気に隣の席のMac miniとWindowsマシンが `ログイン=>動画再生` の操作をテストしてくれています。動画プレイヤーのテストというニッチなアレなのですが、意外にも汎用的なエッセンスが多く含まれていて、入門にいいのでは？と思ったので、ハンズオン形式の記事にしてみた次第です。


# tl;dr
書いてあること

- 動画プレイヤーを再生するE2EテストをKatalon Studioを使ってやってみる
- CIで実行してみる
- Jenkinsで定期実行してヘルスチェックっぽくしてみる
- 令和になってもE2Eはつらい

書いてないこと

- E2Eテストの思想、お作法、ベストプラクティスなど
- Katalon Studioの細かい説明・使い方など


# E2Eテスト
E2Eテストとは簡単に言うと「ブラウザでポチポチする動作を自動で行うことでWebアプリケーション/サイトが正常に動作しているかを確かめる」テストのことです。Webにおけるテストの中で、最も上位の階層に位置するテストです。

本気でやろうとすると大体つらい思いをすることで有名です。


## 動画プレイヤーでもE2Eテストしたい
仮に、とある動画配信サービスを提供しているWebサイトがあり、そのサービスは(サポート切れ間近のもの含めた)各種OS・(サポート切れ間近のもの含めた)各種ブラウザ・(サポート切れ間近のもの含めた)各種ブラウザプラグインをサポートしていて、またユーザーのニーズに応えるため様々な課金体系(従量課金・月額サブスクリプション・無料コンテンツなどなど)があり、さらに作品のジャンルなどに応じてページやドメインが分かれている、としましょう。

おそらくそのサイトの色んなところには、それぞれ少しずつフロント側の実装やプレイヤー自体の実装が異なる動画プレイヤーが埋め込まれていることでしょう。例えばその数が**10**個だとしましょう(実際にはモバイルアプリやテレビ用アプリなども大量にあると思いますが、今回はWebに限定します)。

そしてサポートしているOSがメジャーバージョンの区別を含めて**5**つ、サポートしているブラウザが**5**種類だとします。

さて、これら全てに関わる動画再生ライブラリの修正を行ったとするとどうなるでしょうか？もし網羅するなら`10×5×5=250`個のテストをしないといけないですね！ｱﾊﾊ。

自分はこういったリリース前検証に使いたくてE2Eテストを導入しました。また、**ブラウザのバージョンアップ等によって動画が再生できなくなっていないかをヘルスチェックしたい**、という要求もありました。


# ツール
E2Eテスト関連のツールはかなりたくさんあります。Seleniumをラップしたようなフレームワーク・GUIツールだけでもそんなにいっぱいあるのか・・・という気持ちです。なかなかスクラッチでSeleniumのコードをガリガリ書くのは大変なので、なんらかのツールを使うのはほぼマストな気がします。

- [GUIテストツール一覧](https://qiita.com/jun2014/items/8cabbd52830904af49af)
- [Best Automation Testing Tools for 2020 (Top 10 reviews)](https://medium.com/@briananderson2209/best-automation-testing-tools-for-2018-top-10-reviews-8a4a19f664d2)
- [30 Best GUI Testing Tools For GUI Test Automation](https://www.softwaretestinghelp.com/best-gui-testing-tools/)


## Katalon Studio
今回は[Katalon Studio](https://www.katalon.com/)というツールを使ってみました。E2Eテストツール選定時にありがちな要件と、Katalon Studioがそれらをどう満たすかをまとめてみました。

- **とりあえず無料で運用してみたい**
    - 基本的な機能は無償使用可能。
    - version 7.0.0リリース時から大きな改変があったので注意([参照](https://docs.katalon.com/katalon-studio/docs/license.html))。
- **操作のレコーディング機能が欲しい**
    - ある。
- **リポジトリでテストケースを管理して、PushするとCIでテストしてほしい**
    - [Dockerイメージ](https://hub.docker.com/r/katalonstudio/katalon/)があり、CircleCIで実行可能。
- **定期実行してヘルスチェックを行いたい**
    - テストスイートをコマンドラインから実行可能で、ジョブスケジューラと組み合わせれば定期実行できる。
- **そこそこいい感じのGUIだと嬉しい**
    - Eclipseライクで十分使えるGUI。

Katalon Studioについては、以下の記事で分かりやすく紹介されています。
[Katalon Studioとはどんなソフトウェアか](https://qiita.com/kazurayam/items/aece5d314f84893a0a14)


# ハンズオン
やっていきます。動画プレイヤーには[hls.jsのデモページ](https://hls-js.netlify.com/demo/)でも使いましょう。まぁvideoタグで再生できればなんでもOKです。

使用したバージョンは以下です。Katalon StudioにDockerが追いついていなかったので、少しバージョンがずれています。

- Katalon Studio: v7.2.1
- Docker image(katalonstudio/katalon): v7.1.2 
- OS: macOS 10.14.6
- ChromeDriver: 79.0.3945.36
- Chrome: 79.0.3945.88

## サンプルのリポジトリ
完成形はこちらのリポジトリにまとめてあります。
https://github.com/tinoji/katalon-video-player-test


## レコーディングする
Katalon Studioをダウンロードし、プロジェクトを新規作成します。アカウントのアクティベートが必要なので適当にサインアップします。プロジェクトの作成直後に表示されるヘルプページにある通り、↓このボタンを押すとレコーディングできます。
![スクリーンショット 2019-12-14 22.58.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/eef0ef82-a2f0-a546-a8b5-4fa687c2babf.png)

レコーダーウィンドウが起動します。URLに https://hls-js.netlify.com/demo/ を入力します。URLを入力したあと、右上からChromeアイコンを選んでクリックするとレコーディングが始まります。
![スクリーンショット 2019-12-14 22.59.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/a7ccb67d-47b2-00f7-f364-296f53ca5abc.png)

レコーディングが始まったら、以下の操作を行います。
`プレイヤー部分をクリックして再生を開始 => もう一度クリックして再生を停止 => ブラウザを閉じる`
![スクリーンショット 2019-12-20 14.36.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/738341bb-fb68-1586-d505-6297af8898f3.png)

するとこんな感じに操作が記録されます。OKを押すとテストケースが生成されます。
![スクリーンショット 2019-12-14 23.03.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/11b38d02-c591-765f-ac43-7e103e5cc1b9.png)

### ChromeDriverのダウンロード(optional)
前述のバージョン通りにやると起きませんが、Chromeのバージョン次第では以下のようなエラーが起きることがあります。Seleniumでもよく見るやつです。
<img width="503" alt="スクリーンショット 2019-12-20 17.19.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/15c60c40-6aed-43ed-360a-93b6e30430d5.png">

[公式](https://chromedriver.chromium.org/)からChromeDriverを落として、[Katalon Studioが参照するパス](https://docs.katalon.com/katalon-studio/docs/update-or-replace-web-browser-drivers-and-selenium.html#how-to-replace)に置けばOKです。
・・・と思ったらGUIでポチポチ更新できました。`Tools > Update WebDrivers` です :v:


## Actionの追加
ネットワークが遅くて動画の再生がなかなか開始されない、というような状況を考慮しておきたいので、再生開始/停止の操作の間に適当にDelayを入れます。
![スクリーンショット 2019-12-14 23.22.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/59fe17d3-d756-19ed-85de-784be2643b13.png)


## テストケースの実行
テストケースを実行すると先程の操作が再現され、親の顔より見たBig Buck Bunnyが再生されます。簡単ですね。
<img width="1312" alt="スクリーンショット 2019-12-14 23.25.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/6f9c84ce-6208-0ab0-d345-abd1cb59afe0.png">


## ループを使ってみる
実際にやるときは複数の動画でテストしたいはずです。hls.jsのデモページではm3u8のURLを入れるとその動画が再生できるようになっています。URLは[このへん](https://bitmovin.com/mpeg-dash-hls-examples-sample-streams/)から拾ってくればよいでしょう。

もう一度レコーディングしてみます。`起動 => m3u8のURLを入力 => Applyボタンをクリック => 再生 => 停止`をレコーディングすると、こんな感じになるはずです。
![スクリーンショット 2019-12-14 23.42.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/54ea5111-d3b3-9f13-d392-f953cca1b2f9.png)

`URLを入力 => 再生`の部分を繰り返せば複数の動画を再生できます。こういうときは`For Loop` Statementを使います。
![スクリーンショット 2019-12-14 23.45.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/e358aa18-f7d7-9ca7-b51a-4604c270aa90.png)

さっきの操作をループ配下にドラッグして移動させます。ループはList型にして回します。リストをこんな感じで定義して、、、
![スクリーンショット 2019-12-14 23.58.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/5571aa07-4446-86b2-3a90-6ad5e84c123b.png)

`Set Text`の値は`Variable`に変更し、ループで指定した変数と同名にします。
![スクリーンショット 2019-12-15 0.01.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/e9b74bda-1ce2-704c-f07a-9b45e8e959da.png)

この辺の設定はGUIでやっているとちょっと分かりにくいのですが、コードで見るとなんてことないです。エディタの下の方にある`Script`タブをクリックするとコードが表示されます。

```groovy
WebUI.openBrowser('')

WebUI.navigateToUrl('https://hls-js.netlify.com/demo/')

for (def url : ['https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8'
    , 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8']) {
    WebUI.setText(findTestObject('Object Repository/Page_hlsjs demo/input_here_streamURL'), url)

    WebUI.click(findTestObject('Object Repository/Page_hlsjs demo/div_Persist                                _235cfb'))

    WebUI.click(findTestObject('Object Repository/Page_hlsjs demo/video_Apply_video_1'))
}

WebUI.closeBrowser()
```

なんてことないfor文であることが分かります。ポチポチしていてよく分からなくなったらコードを書いちゃえばいいです。コードを編集すると操作アイテムの方にも反映されます。


## テストの成功条件を変更する
このままでは、動画をクリックして再生ができていなかったとしても、クリックができていればテストが通ってしまいます。本当に再生が行われたかどうかをテストする必要があります。videoタグのプロパティを使う方法ぐらいしか思いつかなかったので、それでやります。

こんなjavascriptを実行すると、再生を停止したところまでの時間が取れます。

```javascript
document.getElementsByTagName("video")[0].played.end(0)
```

![スクリーンショット 2019-12-15 0.22.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/77bab1b1-3f5d-dc78-841b-6f8efa35b6af.png)

全く再生されてないと例外になります。

```
Uncaught DOMException: Failed to execute 'end' on 'TimeRanges': The index provided (0) is greater than or equal to the maximum bound (0).
    at <anonymous>:1:50
```

例外が吐かれた場合はもちろん、「Delayした時間に対して再生した時間が短すぎてもNG」という条件にしてみます。
`Execute JavaScript`というkeyword(Katalon StudioではUI操作を含むあらゆるアクションをkeywordと呼ぶらしい？)を使用して実行し、変数に代入します。GUIで作ってもいいですし、コードで書いてもOKです。コードで書くとこんな感じです。

```groovy
Number playedDuration = WebUI.executeJavaScript('return document.getElementsByTagName("video")[0].played.end(0)', [])
```

`playedDuration`が閾値よりも小さいかどうかは`Verify Less Than` keywordで判断可能です。

```groovy
if (!WebUI.verifyLessThan(THRESHOLD, playedDuration)) {
    // ここでテストを落とす	
}
```

if文の中でテストを落としてメッセージを出すのは[Failure Handling](https://docs.katalon.com/katalon-studio/docs/failure-handling.html#override-failure-handlingbehavior)と、[KeywordUtil](https://docs.katalon.com/katalon-studio/docs/mark-tests-status-for-a-custom-keyword.html)の`markErrorAndStop()`を使えば可能です。packageをインポートする必要がありますが、`Ctrl+Shift+O`で自動インポート可能です(自動ではインポートされないものもあるので注意・・・ :innocent: )。

```groovy
if (!WebUI.verifyLessThan(THRESHOLD, playedDuration, FailureHandling.OPTIONAL)) {
    KeywordUtil.markFailedAndStop("Played duration is too short. Maybe failed to play the video: " + url)
}
```

ちなみに、こういったtipsはフォーラムを探すと見つけやすいです。
[Katalon Community: FailureHandling.OPTIONAL custom message print issue using KeywordUtil](https://forum.katalon.com/t/failurehandling-optional-custom-message-print-issue-using-keywordutil/6795)

Delayの時間と閾値を適当に決めて実行してみると、想定通りの結果になると思います。


## Test Dataを使用する
以上で一通りのテストができるようになりました。あとはテストケースをゴリゴリ増やしていけばいいのですが、リストで定義したURLが増えてくると、一覧性に欠けたりテストケースをまたげなかったりして面倒になってきます。`Test Data`という概念があるのでそれを使うと解決できます。

`Data Files > New > Test Data`から作成します。`Data Type`はExcelファイルとかも使えますが、`Internal Data`にします。
![スクリーンショット 2019-12-15 1.53.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/24ee81db-e2d0-2d02-fb5c-4009e800ac90.png)


`URL`というカラムを作成してURLをリストアップします。
![スクリーンショット 2019-12-15 1.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/269cc1a0-79f7-6070-c003-764d6efb7e26.png)

for文で使うときはこんな感じです。先程のfor文を書き換えて同様に動くことを確認してみてください。

```groovy
InternalData urls = findTestData('test_urls')

for (def index : (0..urls.getRowNumbers() - 1)) {
    String url = urls.internallyGetValue('URL', index)
    ...
}
```

## Custom Keywordを定義する
ちょっとコードが長くなってきました。メソッド化したいところですが、テストケース内に定義すると別のテストケースで使えないので、`Custom Keyword`というものを使ってみます。`Keywords > New > Keyword` で作成します。パッケージを作成することもできるのでその中に作ってもOKです。

```groovy
public class VideoPlayer {
	static final int PLAY_DELAY = 20
	static final int PLAYED_DURATION_THRESHOLD = 5

	/**
	 * Verify video played
	 * @param dataFile data file name
	 */
	@Keyword
	public static void verifyPlayed(String dataFile) {
		InternalData urls = findTestData(dataFile)

		for (def index : (0..urls.getRowNumbers() - 1)) {
			String url = urls.internallyGetValue('URL', index)
			WebUI.setText(findTestObject('Object Repository/Page_hlsjs demo/input_here_streamURL'), url)
			WebUI.click(findTestObject('Object Repository/Page_hlsjs demo/div_Persist                                _235cfb'))
			WebUI.delay(PLAY_DELAY)

			Number playedDuration = WebUI.executeJavaScript('return document.getElementsByTagName("video")[0].played.end(0)', [])
			if (!WebUI.verifyLessThan(PLAYED_DURATION_THRESHOLD, playedDuration, FailureHandling.OPTIONAL)) {
				KeywordUtil.markFailedAndStop("Played duration is too short. Maybe failed to play the video: " + url)
			}
		}
	}
}
```

こんな風にすると、Test Data名を渡せばテストができて、呼び出し元(テストケース)は一行で済みます。

```groovy

VideoPlayer.verifyPlayed('test_urls')
```

ループ自体はテストケースのコード内で回して、メソッドにはURL自体を渡した方がシンプルかもしれません。よしなにどうぞ。

## コマンドラインからテストを実行する
以上でテストのコード自体は大体いい感じになりました。以降でCIや定期実行をやっていくのですが、その際にコマンドラインから実行することになるので、その方法を紹介しておきます。このへんのドキュメントは[こちら](https://docs.katalon.com/katalon-studio/docs/console-mode-execution.html)。

### テストスイートの作成
コマンドラインで実行できるのはここまでで作成した`Test Case`ではなく`Test Suite`(複数のテストケースをまとめたもの)です。まずはこれを作ります。`Test Suites > New > Test Suite` からテストケースを選択するだけです。
<img width="507" alt="スクリーンショット 2019-12-20 15.49.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/494e6f3f-5c08-07b3-536e-b60dfa8e2db1.png">

### コマンドの生成
作成したら、テスト実行ボタンの近くにあるターミナルっぽいボタンを押します。
<img width="186" alt="スクリーンショット 2019-12-20 15.51.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/e2ff18b7-9e35-cc2e-57d0-cbf59eba8f78.png">

テストスイートを選択してGenerate Commandします。`Run with`はChromeで。
<img width="687" alt="スクリーンショット 2019-12-20 15.52.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/be1fb983-c581-c7c0-204a-07e933edd650.png">

こんなコマンドが出力されます。

```shell
./katalonc -noSplash -runMode=console -projectPath="/Users/kikuchi-hiroaki/Desktop/katalon-video-player-test/katalon-video-player-test.prj" -retry=0 -testSuitePath="Test Suites/TestSuite01" -executionProfile="default" -browserType="Chrome" -apiKey="**********"
```

ドキュメントに書いてありますが、バージョン6まではKatalon Studio自体のバイナリでコマンドラインから実行できたのが、バージョン7からKatalon Studio Engineが必要になりました。ダウンロードページからダウンロードしてApplicationsディレクトリに移動しておきます。

Macの場合`katalonc`は`/Applications/Katalon\ Studio\ Engine.app/Contents/MacOS`にあるので、そこにcdするかフルパスを指定して使用します。

ターミナルから実際に実行して動作を確認します。Katalon Studioから行っていた動作が再現されて、テストケースをPassすればOKです。


## CIでテストを回す
リポジトリにテストケースを追加していく形になりますが、「テストケース(あるいはコード)自体が間違っている」という状況を防止するためにも、PRを出したタイミングなどにCIでチェックしてほしいところです。

[Katalon StudioにはDockerイメージがある](https://hub.docker.com/r/katalonstudio/katalon/)ので、これを使用してCircleCIでテストを回してみます。もちろんGitHub Actionsを使ってもOKです。Dockerイメージの使い方は[README](https://github.com/katalon-studio/docker-images)に書いてあります。イメージのバージョンによってコマンドが異なるので注意です。7.1.2ではこんな感じです。

```shell
katalon-execute.sh -browserType="Chrome" -retry=0 -statusDelay=15 -testSuitePath="Test Suites/TestSuite01"
```

執筆時点ではドキュメントやREADMEに記載がないのですが、先程と同様にAPI Keyが必要です。CircleCIの環境変数に設定して以下を追加します。

```shell
-apiKey=$KATALON_API_KEY
```

CircleCIでビルドして通ればOKです :ok_hand:  


## 定期実行でヘルスチェック
最後に定期実行をやってみます。色々方法はあると思いますが今回はJenkinsを使います。
(※Jenkins自体のインストール・セットアップは割愛)

と言っても、`git pull=>さっきのコマンドを実行`をやるだけです。フリースタイルプロジェクトを作成します。
<img width="976" alt="スクリーンショット 2019-12-23 12.55.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/e842e9e4-4732-e2e2-6ab7-31dccda0742d.png">

「ソースコード管理」でリポジトリのURLを指定します。
<img width="962" alt="スクリーンショット 2019-12-23 12.57.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/c2ad2fc4-295d-dd8f-839e-fc87686c3a7a.png">

定期実行したいので「ビルド・トリガ」は「定期的に実行」にします。
<img width="959" alt="スクリーンショット 2019-12-23 12.58.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/e17b2eb6-901e-6ec3-44b9-8022673f0480.png">

「ビルド」に「シェルの実行」を追加し、コマンドラインで実行したときのコマンドを設定します。
![スクリーンショット 2019-12-23 13.59.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130232/2f8aceb0-e2d9-1145-ae9e-604d8fae3064.png)

ただし、リポジトリはJenkinsのワークスペースディレクトリ配下にダウンロードされるので、`-projectPath`オプションだけ変更する必要があります。一度適当に設定してビルドしてみて、リポジトリの場所を特定してからやってもいいです。

```shell
/Applications/Katalon\ Studio\ Engine.app/Contents/MacOS/katalonc -noSplash -runMode=console -projectPath="/Users/kikuchi-hiroaki/.jenkins/workspace/katalon-video-player-test/katalon-video-player-test.prj" -retry=0 -testSuitePath="Test Suites/TestSuite01" -executionProfile="default" -browserType="Chrome" -apiKey="***********"
```

定期実行を設定しましたが、動作を確認したいのでプロジェクトを保存した後「ビルド実行」を手動でやってみます。
ビルドが成功し、コンソール出力を見てテストが通ってそうならOKです。JUnitのレポートが`Reports`ディレクトリあたりに保存されるので見てみます。PASSEDになってますね :tada: 

```xml
<testcase name="Test Cases/Chrome" time="97.306" classname="Test Cases/Chrome" status="PASSED">
```

### プラグイン
Jenkinsに入れておくとよさそうなプラグインを挙げておきます。

- [Slack Notification](https://plugins.jenkins.io/slack)
    - ビルド結果をSlackに通知するのに使用。
- [Naginator](https://wiki.jenkins.io/display/JENKINS/Naginator+Plugin)
    - ビルドを再スケジュールするために使用。
    - E2Eテストは往々にして不安定で、問題がなくても落ちてしまうことがあるので、何回かリトライさせてます。


## 異なる環境でのテスト
開発環境やステージング環境でテストしたい場合は、例えば[プロキシ](https://docs.katalon.com/katalon-studio/docs/proxy-preferences.html)が使用できます。[コマンドオプション](https://docs.katalon.com/katalon-studio/docs/console-mode-execution.html#proxy-options)もあるので、さっきのコマンドに追加すればOKです。

hostsで切り替えることもできますが、Jenkinsでやろうとするとそこそこ面倒です。古典的にやるなら、hostsファイルをスイッチするシェルスクリプトを用意してJenkinsでテスト前に実行するとかですかねぇ。Win/Macともに権限周りが面倒ですが・・・。


# お約束: つらいところ
お疲れ様でした〜！
E2Eテストの話をしたらつらいことを述べる、というのが業界のお約束な気がするので、最後にまとめておきます。

- GUIで簡単にできると思いきや、いつの間にかガッツリコード書いてる感
    - ハンズオンでお察しの通り。
    - とは言ってもスクラッチでSeleniumのコードを書くよりKatalonのライブラリを使うほうがまだ楽なので・・・。
- クロスプラットフォーム・クロスブラウザ
    - つらい。闇。
    - 例えばWindows+ChromeではChromeDriver設定におまじないが必要だったり、当然ブラウザごとに仕様が違ったり(動画のautoplay周りとかだるかったです)。
    - 参照: [クロスブラウザテストの闇と闇と闇](https://speakerdeck.com/tsuemura/kurosuburauzatesutofalsean-toan-toan)
- Katalonの公式Dockerイメージ
    - なぜか運用がとても雑です。
    - masterブランチ1本運用(masterのCIがよく落ちてる)、ドキュメントとの乖離など、ちょっとお粗末な点が目立ちます。
    - (1人でメンテしてて大変そうなので協力してあげるとよさそう・・・)
- メンヘラと付き合っているかのような感覚
    - すごく、丁寧に、ミスのないように、扱わないと、ダメです。