22
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DMMグループAdvent Calendar 2019

Day 24

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

Last updated at Posted at 2019-12-23

本記事はDMMグループ Advent Calendar 2019の24日目の記事です。

どうも、DMM.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のコードをガリガリ書くのは大変なので、なんらかのツールを使うのはほぼマストな気がします。

Katalon Studio

今回はKatalon Studioというツールを使ってみました。E2Eテストツール選定時にありがちな要件と、Katalon Studioがそれらをどう満たすかをまとめてみました。

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

Katalon Studioについては、以下の記事で分かりやすく紹介されています。
Katalon Studioとはどんなソフトウェアか

ハンズオン

やっていきます。動画プレイヤーにはhls.jsのデモページでも使いましょう。まぁ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

レコーダーウィンドウが起動します。URLに https://hls-js.netlify.com/demo/ を入力します。URLを入力したあと、右上からChromeアイコンを選んでクリックするとレコーディングが始まります。
スクリーンショット 2019-12-14 22.59.32.png

レコーディングが始まったら、以下の操作を行います。
プレイヤー部分をクリックして再生を開始 => もう一度クリックして再生を停止 => ブラウザを閉じる
スクリーンショット 2019-12-20 14.36.02.png

するとこんな感じに操作が記録されます。OKを押すとテストケースが生成されます。
スクリーンショット 2019-12-14 23.03.29.png

ChromeDriverのダウンロード(optional)

前述のバージョン通りにやると起きませんが、Chromeのバージョン次第では以下のようなエラーが起きることがあります。Seleniumでもよく見るやつです。
スクリーンショット 2019-12-20 17.19.28.png

公式からChromeDriverを落として、Katalon Studioが参照するパスに置けばOKです。
・・・と思ったらGUIでポチポチ更新できました。Tools > Update WebDrivers です :v:

Actionの追加

ネットワークが遅くて動画の再生がなかなか開始されない、というような状況を考慮しておきたいので、再生開始/停止の操作の間に適当にDelayを入れます。
スクリーンショット 2019-12-14 23.22.17.png

テストケースの実行

テストケースを実行すると先程の操作が再現され、親の顔より見たBig Buck Bunnyが再生されます。簡単ですね。
スクリーンショット 2019-12-14 23.25.17.png

ループを使ってみる

実際にやるときは複数の動画でテストしたいはずです。hls.jsのデモページではm3u8のURLを入れるとその動画が再生できるようになっています。URLはこのへんから拾ってくればよいでしょう。

もう一度レコーディングしてみます。起動 => m3u8のURLを入力 => Applyボタンをクリック => 再生 => 停止をレコーディングすると、こんな感じになるはずです。
スクリーンショット 2019-12-14 23.42.02.png

URLを入力 => 再生の部分を繰り返せば複数の動画を再生できます。こういうときはFor Loop Statementを使います。
スクリーンショット 2019-12-14 23.45.07.png

さっきの操作をループ配下にドラッグして移動させます。ループはList型にして回します。リストをこんな感じで定義して、、、
スクリーンショット 2019-12-14 23.58.14.png

Set Textの値はVariableに変更し、ループで指定した変数と同名にします。
スクリーンショット 2019-12-15 0.01.06.png

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

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を実行すると、再生を停止したところまでの時間が取れます。

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

スクリーンショット 2019-12-15 0.22.20.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です。コードで書くとこんな感じです。

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

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

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

if文の中でテストを落としてメッセージを出すのはFailure Handlingと、KeywordUtilmarkErrorAndStop()を使えば可能です。packageをインポートする必要がありますが、Ctrl+Shift+Oで自動インポート可能です(自動ではインポートされないものもあるので注意・・・ :innocent: )。

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

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

Test Dataを使用する

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

Data Files > New > Test Dataから作成します。Data TypeはExcelファイルとかも使えますが、Internal Dataにします。
スクリーンショット 2019-12-15 1.53.16.png

URLというカラムを作成してURLをリストアップします。
スクリーンショット 2019-12-15 1.48.59.png

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

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です。

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名を渡せばテストができて、呼び出し元(テストケース)は一行で済みます。


VideoPlayer.verifyPlayed('test_urls')

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

コマンドラインからテストを実行する

以上でテストのコード自体は大体いい感じになりました。以降でCIや定期実行をやっていくのですが、その際にコマンドラインから実行することになるので、その方法を紹介しておきます。このへんのドキュメントはこちら

テストスイートの作成

コマンドラインで実行できるのはここまでで作成したTest CaseではなくTest Suite(複数のテストケースをまとめたもの)です。まずはこれを作ります。Test Suites > New > Test Suite からテストケースを選択するだけです。
スクリーンショット 2019-12-20 15.49.58.png

コマンドの生成

作成したら、テスト実行ボタンの近くにあるターミナルっぽいボタンを押します。
スクリーンショット 2019-12-20 15.51.09.png

テストスイートを選択してGenerate Commandします。Run withはChromeで。
スクリーンショット 2019-12-20 15.52.52.png

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

./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イメージがあるので、これを使用してCircleCIでテストを回してみます。もちろんGitHub Actionsを使ってもOKです。Dockerイメージの使い方はREADMEに書いてあります。イメージのバージョンによってコマンドが異なるので注意です。7.1.2ではこんな感じです。

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

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

-apiKey=$KATALON_API_KEY

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

定期実行でヘルスチェック

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

と言っても、git pull=>さっきのコマンドを実行をやるだけです。フリースタイルプロジェクトを作成します。
スクリーンショット 2019-12-23 12.55.42.png

「ソースコード管理」でリポジトリのURLを指定します。
スクリーンショット 2019-12-23 12.57.38.png

定期実行したいので「ビルド・トリガ」は「定期的に実行」にします。
スクリーンショット 2019-12-23 12.58.44.png

「ビルド」に「シェルの実行」を追加し、コマンドラインで実行したときのコマンドを設定します。
スクリーンショット 2019-12-23 13.59.30.png

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

/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:

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

プラグイン

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

  • Slack Notification
    • ビルド結果をSlackに通知するのに使用。
  • Naginator
    • ビルドを再スケジュールするために使用。
    • E2Eテストは往々にして不安定で、問題がなくても落ちてしまうことがあるので、何回かリトライさせてます。

異なる環境でのテスト

開発環境やステージング環境でテストしたい場合は、例えばプロキシが使用できます。コマンドオプションもあるので、さっきのコマンドに追加すればOKです。

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

お約束: つらいところ

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

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?