Selenium
SeleniumIDE
テスト自動化
KatalonStudio

Katalon Studioでテストを自作した

はじめに

わたしはWebアプリケーションのUIをテストする作業をソフトウェアで自動化する技術に関心があります。2018年1月以来わたしは Katalon Studio というソフトウェアに注目しています。以前「Katalon Studioとはどんなソフトウエアか」で概要を紹介しました。そして前回はRecord機能を使ってコードを自動生成しました。

今回は前回自動生成したKatalon Studioプロジェクトを手直しします。小規模だが自動化テストとよべる内容を備えていてエラー無しに動くKatalon Studioプロジェクトに仕立てます。

今回すること

前回「Katalon StudioでRecordしてみた」においてテスト一式をRecord機能を使って自動生成しました。ここで生成されたコードを調べてみるとオブジェクトの名前が不適当だったり、HTML内の特定の要素を特定するSelectorとしてのXPath式が冗長だったり。改善すべき箇所がたくさんあるのがわかります。そこで前回自動生成したコードをリファクタリングすることから始めます。外から見た動作仕様を変更せずにテスト・コードを書きかえます。オブジェクトの名前を分かりやすいものに変更し、Selectorを最適化していきます。

リファクタリングが済んだら次に verification すなわち「検証」の動作を導入します。Webページ間の遷移がちゃんとできているか、ページに表示された文字データが正しいものかどうかをチェックして、もしもダメだったらエラーを報告することをKatalon Studioにやらせます。

前提条件

サンプル in GitHub

この記事が説明するKatalon Projectのファイル一式をGitHubにアップしました。

このレポジトリのmasterブランチをローカルにcloneするにはこうします。

$ cd ~
$ mkdir katalon-workspace
$ cd katalon-workspace
$ git clone https://github.com/kazurayam/CURA-Test-Project.git

このレポジトリにはmasterプランチだけでなく、途中経過を保存したブランチがいくつかあります。

ブランチ名 内容
step0 Recordで自動生成したばかり。リファクタリングする前。verification無し。Runすれば成功する
step1 Object Repositoryにページ毎サブフォルダ4つを追加し、Test Object群を配置し直した。Test Objectの名前はもとのまま
step2 Test Objectの名前を見直した
step3 Test Objectのselectorを見直した
step4 不要なTest Objectを削除した
step5 ページ遷移のチェックを実装した
step6 データのチェックを実装した
step7 わざとverifyEqualがFAILするようにした、LogViewerの表示を確認するため
master 最終形

リモートのstep0ブランチをローカルに取り出すには下記の操作をします。

$ git checkout -b step0 origin/step0 

用語の定義

用語 定義
AUT Application Under Testの略。テスト・コードが対象としているWebアプリケーション
Test Object Object Repositoryのなかに作られたモノを集合的に Test Objectと呼ぶ。Test ObjectはTest CaseがアクセスしたいHTML要素がページの中のどこにあるかを示すSelector式のコンテナである。 スクリーンショット 2018-05-05 16.25.42.png
Test Case テストの処理手順を具体的に記述したGroovyスクリプトである。 スクリーンショット 2018-05-05 16.30.01.png

準備

「Katalon StudioでRecordして見た」を参考に あなたのPC上に CURA Test Project を作ってください。ないしは https://github.com/kazurayam/CURA-Test-Project をローカルにcloneしてブランチstep0をcheckoutしてください。

Object Repositoryをリファクタリングする

Record機能が生成したCURA Test Projectのコードを調べてみるとイマイチなところがたくさんあります。リファクタリングしてスッキリさせましょう。

Test ObjectをWebページ毎に仕分けしよう

Webサイト CURA Healthcare Service は次の4つのURLから構成されていました。

一方でRecord機能がObject Repositoryの中にどんなTest Objectを生成したかというと、 Page_CURA Healthcare Service というフォルダをひとつ生成して、その中に20個弱のTest Objectを出力しました。下記のスクリーンショットのとおり。

スクリーンショット 2018-05-04 6.42.04.png

AUTが4つのページで構成されているのだから、それと1対1に対応するようにObject Repositoryの下にページ毎に4つのフォルダを設けてTest Objectを再配置しましょう。そのほうが分かりやすいから。

ページ フォルダ名
ホーム・ページ Page_CuraHomepage
ログイン・ページ Page_Login
受診予約の入力ページ Page_CuraAppointment
受診予約の確認ページ Page_AppointmentConfirmation

Object Repositoryをマウスで右クリックすると操作メニューが展開します。New > Folder と選択してフォルダを追加しましょう。
スクリーンショット 2018-05-04 7.07.01.png

こうなりました。

スクリーンショット 2018-05-04 8.43.14.png

Test Objectをどのフォルダに移すべきか?を考える

たとえば Page_CURA Healthcare Service/input_password というTest Objectをダブルクリックすれば画面右ペインに属性が表示されます。

スクリーンショット 2018-05-04 9.54.51.clipped.png

Object Propertiesの内容を見ればこのTest Objectに対応するHTML要素が <input id="txt-username" name="username"> であるとわかります。この<input>タグがAUTのどのページにあったっけ?と考えます。Firefoxブラウザで http://devaut.katalon.com/ を開き PF12 キーを押して開発者ツールを起動してページのHTMLコードを調べましょう。 ... ログイン・ページのなかに<input id="txt-username"がありました。
スクリーンショット 2018-05-04 10.23.47.png

だからTest Object Page_CURA Healthcare Service/input_passwordPage_Login/input_password へ移動するのが適切だと判断できます。

こんなふうにしてTest Objectひとつひとつの氏素性を調べどのサブフォルダに移動すべきかを判断します。Test Objectをぜんぶ調べ終わった時、あなたはきっとRecord機能が自動生成したテスト・コードの全体を把握できているでしょう。

ドラッグ&ドロップでTest Objectを移動する

ページ毎のフォルダにRecord機能が自動生成したTest Objectたちを移動しましょう。ドラッグ&ドロップできます。例えば Page_CURA Healthcare Service/input_passwordPage_Login/input_password へ移動しましょう。

Test Objectをドラッグ&ドロップするとKatalon StudioがTest Case Basic のコードを自動的に修正します。例えばこれ:

WebUI.setText(findTestObject('Page_CURA Healthcare Service/input_password'), 'ThisIsNotAPassword')

がこんなふうに

WebUI.setText(findTestObject('Page_Login/input_password'), 'ThisIsNotAPassword')

自動的に修正されます。eclipseのJavaエディタでリファクタリング機能のrename classしたような感じです。

Page_CURA Healthcare Serviceサブフォルダの中にあった20個弱のTest Objectをぜんぶページ別のサブフォルダへ移しましょう。空になったPage_CURA Healthcare Serviceサブフォルダは不要ですから削除しましょう。下記スクリーンショットのようになりました。

スクリーンショット 2018-05-04 10.59.07.png

Test CaseをRunする

Test Objectを移動しました。それに伴ってTest Caseも自動的に変更されました。ここでTest Caseを実行してまともに動くかどうかを確かめましょう。Test Caseを実行する操作はこちらを参照のこと。

step1

以上でTest Object群を4つのWebページに対応する形に整理することができました。

この段階をGitHub上のCURA-Testing-Projectのstep1ブランチとして保存しました。step1ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step1 origin/step1 

Test Objectの名前を改善しよう

Rename機能が自動生成したTest Objectの名前を見直しましょう。a_Make Appointmentinput_passwordという名前は適切です。そのままで良いでしょう。しかし p_30052018 とか p_Yes とかいう名前はどうもいただけません。なんじゃこりゃ?と思ってしまう。

スクリーンショット 2018-05-04 11.24.39.png

なぜこんな名前になったのか?Test Object p_30052018 に対応するHTML要素はこれでした。

        <p id="visit_date">30/05/2018</p>

"30/05/2018"という内容を持つpタグでした。スラッシュ文字(/)はOSのファイルパスの区切りとして特殊な意味を持つ文字なのでオブジェクトの名前の一部として使うのは避けたほうがいい。だからRecord機能は p_30052018 という名前を作りだしたのでしょう。しかし <p id="visit_date" というふうにID属性があるのだからそれを根拠とする名前の方が適切です。つまり p_30052018 よりも p_visit_date が好ましい。

Test Objectの名前を変更することができます。たとえRecord機能が決めた名前でも変更してかまいません。 p_30052018 をマウスで右クリックしてメニューを開きRenameしましょう。

スクリーンショット 2018-05-04 13.07.24.clipped.png

こんなふうにしてTest Objectの名前をひとつひとつ見直していきます。HTML要素がid属性を持っているならそれをTest Objectの名前の一部として転用するのが得策でしょう。

Test Objectの名前をぜんぶ見直した結果こんなふうになりました。
スクリーンショット 2018-05-04 13.11.14.clipped.png

step2

この段階をGitHub上のCURA-Testing-Projectのstep2ブランチとして保存しました。step2ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step2 origin/step2 

Test ObjectのSelectorを改善しよう

Test Object Page_CuraHomepage/a_Make Appointment のSelectorがこうなっていました。

//a[@id = 'btn-make-appointment' and @href = './profile.php#login' and (text() = 'Make Appointment' or . = 'Make Appointment')]

スクリーンショット 2018-05-04 13.23.30.png

このSelectorはダメだ。冗長すぎる。シンプルにしましょう。

  1. and @href = './profile.php#login'の部分は無いほうが良い。これがあると将来Webアプリの設計が変更されてログイン・ページのURLが ./login.php に変わった場合にこのテストも一緒に修正しなければならない。もし修正し忘れるとテストが失敗するだろう。
  2. //a[@id = 'btn-make-appointment' and (text() = 'Make Appointment' or . = 'Make Appointment')]のようにid属性による選択と内容文字列による選択の二つをAND条件で結合する必要はない。どちらか片方あれば十分だ。

こう考えれば次の2つのうちいづれかを選択することになるでしょう。

  1. //a[@id = 'btn-make-appointment']
  2. //a[text() = 'Make Appointment' or . = 'Make Appointment']

AUTの仕様によりid属性の値が固定であって頼りにできるならば#1が良いでしょう。ところがWebアプリケーションの種類によっては(たとえばAngularとか)id属性の値が可変な場合があります。可変なidはTest ObjectのSelectorの根拠として使いづらい。id属性が可変な場合にはむしろ#2のようにHTML要素の内容文字列をSelectorの根拠とする方が具合がいい。

AUTがどう設計されているかを考慮に入れながら適切にSelectorを修正していきます。

ここでは#2を選択することにします。つまりSelectorを //a[text() = 'Make Appointment' or . = 'Make Appointment'] に変更したい。そのためには Test Object Page_CuraHomepage/a_Make Appointment の設定を変更します。下記のスクリーンショットを参照。 Detect object by? のチェックボックスをon/offしてtagとtextを根拠として選択します。チェックボックスのon/offを更新する都度selector式が更新されるのが分かります。

スクリーンショット 2018-05-04 14.00.16.png

step3

同様のやり方でぜんぶのTest ObjectのSelectorをシンプルなものに変更しました。この段階をGitHub上のCURA-Testing-Projectのstep3ブランチとして保存しました。step3ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step3 origin/step3 

不要なTest Objectを削除しよう

td_30という名前のTest ObjectがRecord機能によって自動されていました。

スクリーンショット 2018-05-04 14.56.30.png

わたしが予約ページで受診日を入力しようとtextフィールドにマウスをのせたら、JavaScriptが動いてDate Chooserの小窓の中から日付を選べと促してきた。そこでわたしは30日をクリックしました。その操作がrecordされた結果として td_30 が生成されたのでした。

スクリーンショット 2018-05-04 15.02.43.clipped.png

じつはTest Object td_30 をTest Case Basic は利用しません。日付を input_visit_date に文字を直接設定してしまいます。こんなふうに:

WebUI.setText(findTestObject('Page_CuraAppointment/input_visit_date'), '30/05/2018')

だからRecord機能によって生成された Test Object td_30 は不要です。削除してしまいましょう。

あなたが自分のPCでKatalon StudioのRecordを実行した時にWebブラウザのウインドウ内部をクリックすることでしょう。何の気なしに何回もクリックしてしまう。するとそのクリック1回につきRecord機能がTest Objectをひとつ自動生成するでしょう。結果的に、あとで振り返ってみると何のためにこれが生成されたのかよくわからないTest Objectがたくさんできている、ということがありえます。あなたがクリックしたからです。理由はそれだけ。Test Objectの氏素性を見極めて不要と判断できたら、削除してしまいましょう。

step4

この段階をGitHub上のCURA-Testing-Projectのstep4ブランチとして保存しました。step4ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step4 origin/step4

以上でリファクタリングが完了しました。

検証を実装する

CURA-Testing-Projectのstep4ブランチのコードには検証のためのコードがひとつもありません。あなたがブラウザで http://demoaut.katalon.com/ を開いてマウスとキーボードで操作したのをリプレイできるが、それだけです。これでは 自動化されたテスト(automated test)とはいえません。そこで次に検証のためのコードを作りましょう。

ページの遷移を検証しよう

http://demoaut.katalon.com/ のサイトは下記の図のように4つのページで構成されていてボタンをクリックすることによりページからページへ遷移します。

page transition.png

この図には5本の矢印があります。矢印はページ間の遷移を表しています。ボタンのクリックによってねらいどおりのページに遷移できたかどうかを確かめたい。どうやって確かめるか?

遷移先のページの中に存在しているはずのHTML要素がいま現在表示中のページの中にあるかどうかをTest Caseが調べればいい。 それによってページ遷移が成功したかどうかを判断することができます。

各ページを特徴付けるHTML要素を特定します。たとえばホーム・ページには MakeAppointment.pngボタンがある。そこでTest Caseがホーム・ページを開いたあとでいま現在表示されているページの中に Make Appointmentボタンが存在するかどうかをチェックできればいい。いま表示されているページのなかに Make Appointmentボタンが存在しているかどうかをチェックするには verifyElementPresentキーワードを使えばよい。

そこでTest Case Basicを下記のように記述しました。

WebUI.openBrowser('')

WebUI.navigateToUrl('http://demoaut.katalon.com/')

// Make Appointmentボタンがあるか?
WebUI.verifyElementPresent(findTestObject('Page_CuraHomepage/a_Make Appointment'),
    10, FailureHandling.STOP_ON_FAILURE)

このコードは次のことをしています。

  1. ブラウザを立ち上げる
  2. ブラウザで http://demoaut.katalon.com/ を開く
  3. ページのなかに Make Appointmentボタンがあるかどうか調べる。ページが表示されるのに若干時間がかかるのを考慮して最大10秒間待つ。もしも Make Appointmentボタンが見つからなければログにFAILEDと出力したうえで処理を中断する。FAILED発生後の動きとしてFailureHandling.STOP_ON_FAILUREを指定している。

同じ方法を上図の5つの矢印すべてに適用することができます。

step5

この段階をGitHub上のCURA-Testing-Projectのstep5ブランチとして保存しました。step5ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step5 origin/step5 

表示されたデータを検証しよう

確認画面に受信予約の内容が表示されます。

スクリーンショット 2018-05-05 15.33.14.clipped.png

ここに表示されたデータが妥当であるかどうか?それを検証するコードを書きましょう。

どういう条件が満足されればデータが妥当だと判断するのか、その仕様を決める必要があります。ここでは下記の通りとします。

Test Object名 データの検証条件
p_facility 次の3通りの文字列いづれかと一致すること
Tokyo CURA Healthcare Center
Hongkong CURA Healthcare Center
Seoul CURA Healthcare Center
p_hospital_readmission YesNo のいづれかと一致すること
p_program MedicareMedicaidNone のいづれかと一致すること
p_visit_date dd/MM/yyyyの形式の日付であること。今日よりも未来の日付であること。日曜日でないこと。
p_comment 未入力でもOK。もし入力があるなら400文字以下であること。

許される値と一致するかどうかのチェック --- 正規表現を使う

ページのなかのpタグの内容文字列として表示されているテキストを取り出すにはgetTextキーワードを使います。そしてテキストを正規表現と照合してマッチするかどうかを判定するのにverifyMatchキーワードを使うことができます。

例えば p_facility つまり施設の名前として表示されているテキストが妥当かどうかの検証を下記のようなGroovyコードで記述することができます。

def facility = WebUI.getText(findTestObject('Page_AppointmentConfirmation/p_facility'))
WebUI.verifyMatch(facility,
    '^(Tokyo|Hongkong|Seoul) CURA Healthcare Center$', true)

正規表現を使ったデータの妥当性チェックは多くの場面で応用が効きます。p_hospital_readmissionとp_programについても正規表現によるチェックが適用できます。

日付の詳細なチェック --- Groovyコードで補強する

p_visit_dateつまり受診する予定の日付に関して「dd/MM/yyyyの形式の日付であること、今日よりも未来の日付であること、日曜日でないこと」というチェックを行いたいのですが、このチェックを実現するキーワードがKatalon Studioにあらかじめ組み込まれてはいません。しかし対処する方法があります。

Katalon StudioのTest Caseつまりテスト手順を具体的に記述したモノは実はGroovyスクリプトです。Groovy言語の文法にしたがって正しく書けばTest Caseのなかでなんでも記述できます。 java.time.LocaleDateTime などのJava8の日付時刻APIをTest Caseのなかで使って日付データに関する詳細なチェックを記述することができます。

こんなコードです。

def visitDateStr2 = WebUI.getText(findTestObject('Page_AppointmentConfirmation/p_visit_date'))
WebUI.verifyMatch(visitDateStr2,
    '[0-9]{2}/[0-9]{2}/[0-9]{4}',
    true, FailureHandling.CONTINUE_ON_FAILURE)

import java.time.temporal.TemporalAccessor
TemporalAccessor parsed = 
    DateTimeFormatter.ofPattern('dd/MM/uuuu').parse(visitDateStr2)
import java.time.LocalDate
LocalDateTime visitDate2 = LocalDate.from(parsed).atStartOfDay()
// 今日よりも未来の日付であること
boolean isAfterNow = visitDate2.isAfter(LocalDateTime.now())
WebUI.verifyEqual(isAfterNow, true,
                  FailureHandling.CONTINUE_ON_FAILURE)

// 日曜日ではないこと
def dayOfWeek = DateTimeFormatter.ofPattern('E').withLocale(Locale.US).format(parsed)
WebUI.verifyNotEqual(dayOfWeek, 'Sun')

コメント欄(p_comment)にかんするチェック「未入力でもOK。もし入力があるなら400文字以下であること」もGroovyスクリプトで下記のように記述することができます。

def comment = WebUI.getText(findTestObject('Page_AppointmentConfirmation/p_comment'))
if (comment != null) {
    WebUI.verifyLessThan(comment.length(), 400)
}

step6

この段階をGitHub上のCURA-Testing-Projectのstep6ブランチとして保存しました。step6ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step6 origin/step6 

LogViewerをチェックして検証の失敗に気づく

WebUI.verifyMatch()] とか WebUI.verifyElementPresent() などのKatalon Studio組み込みのキーワードを使って検証を実行した時に失敗したら(FAILED)どうなるか? Katalon Studioの画面上のどこに失敗が表示されるのでしょうか?

Test Case Basic を実行した時、Katalon Studioの画面の右下にログが表示されます。こんなふうに:
スクリーンショット 2018-05-12 11.21.20.png

もしも FAILED があれば赤いバツ印が表示されます。こんなふうに:
スクリーンショット 2018-05-12 11.38.36.png

FAILEDを見つけたら即刻対処しましょう。

step7

FAILEDの表示を見たかったのでわざとverifyが失敗するようにTest Case Basicを1行書きかえました。

// 日曜日ではないこと
def dayOfWeek = DateTimeFormatter.ofPattern('E').withLocale(Locale.US).format(parsed)
//WebUI.verifyNotEqual(dayOfWeek, 'Sun')      //本当はこっち
WebUI.verifyEqual(dayOfWeek, 'Sun')           //わざと失敗させてみた

この段階をGitHub上のCURA-Testing-Projectのstep6ブランチとして保存しました。step6ブランチをローカルに取り出すには下記の操作をします。

$ cd ~/katalon-workspace/CURA-Test-Project
$ git checkout -b step7 origin/step7 

結び

今回は、ページ間遷移がちゃんとできているかどうか、ページに表示されたデータが妥当かどうかのチェックを行う自動化テストをKatalon Studioで自作しました。

次回はKatalon Studioが実現している下記のfeaturesを紹介するつもりです。

  • Test Caseのモジュール化
  • GlobalVariable
  • Execution Profile --- ひとつのProjectで複数環境をテストする
  • Test Suite
  • Report