はじめに
わたしは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にやらせます。
前提条件
- macOS High Siera 10.16,
- Katalon Studio 5.4.1がインストール済みであること。まだならば「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
用語の定義
|
準備
「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から構成されていました。
- http://demoaut.katalon.com/ ホーム・ページ
- http://demoaut.katalon.com/profile.php#login ログイン・ページ
- http://demoaut.katalon.com/#appointment 受診予約の入力ページ
- http://demoaut.katalon.com/appointment.php#summary 受診予約の確認ページ
一方でRecord機能がObject Repositoryの中にどんなTest Objectを生成したかというと、 Page_CURA Healthcare Service
というフォルダをひとつ生成して、その中に20個弱のTest Objectを出力しました。下記のスクリーンショットのとおり。
AUTが4つのページで構成されているのだから、それと1対1に対応するようにObject Repositoryの下にページ毎に4つのフォルダを設けてTest Objectを再配置しましょう。そのほうが分かりやすいから。
| ページ | フォルダ名 |
|:--|:--|:--|
| ホーム・ページ | Page_CuraHomepage |
| ログイン・ページ | Page_Login |
| 受診予約の入力ページ | Page_CuraAppointment |
| 受診予約の確認ページ | Page_AppointmentConfirmation |
Object Repositoryをマウスで右クリックすると操作メニューが展開します。New > Folder
と選択してフォルダを追加しましょう。
こうなりました。
Test Objectをどのフォルダに移すべきか?を考える
たとえば Page_CURA Healthcare Service/input_password
というTest Objectをダブルクリックすれば画面右ペインに属性が表示されます。
Object Propertiesの内容を見ればこのTest Objectに対応するHTML要素が <input id="txt-username" name="username">
であるとわかります。この<input>
タグがAUTのどのページにあったっけ?と考えます。Firefoxブラウザで http://devaut.katalon.com/ を開き PF12 キーを押して開発者ツールを起動してページのHTMLコードを調べましょう。 ... ログイン・ページのなかに<input id="txt-username"
がありました。
だからTest Object Page_CURA Healthcare Service/input_password
は Page_Login/input_password
へ移動するのが適切だと判断できます。
こんなふうにしてTest Objectひとつひとつの氏素性を調べどのサブフォルダに移動すべきかを判断します。Test Objectをぜんぶ調べ終わった時、あなたはきっとRecord機能が自動生成したテスト・コードの全体を把握できているでしょう。
ドラッグ&ドロップでTest Objectを移動する
ページ毎のフォルダにRecord機能が自動生成したTest Objectたちを移動しましょう。ドラッグ&ドロップできます。例えば Page_CURA Healthcare Service/input_password
を Page_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
サブフォルダは不要ですから削除しましょう。下記スクリーンショットのようになりました。
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 Appointment
やinput_password
という名前は適切です。そのままで良いでしょう。しかし p_30052018
とか p_Yes
とかいう名前はどうもいただけません。なんじゃこりゃ?と思ってしまう。
なぜこんな名前になったのか?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しましょう。
こんなふうにしてTest Objectの名前をひとつひとつ見直していきます。HTML要素がid属性を持っているならそれをTest Objectの名前の一部として転用するのが得策でしょう。
Test Objectの名前をぜんぶ見直した結果こんなふうになりました。
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')]
このSelectorはダメだ。冗長すぎる。シンプルにしましょう。
-
and @href = './profile.php#login'
の部分は無いほうが良い。これがあると将来Webアプリの設計が変更されてログイン・ページのURLが./login.php
に変わった場合にこのテストも一緒に修正しなければならない。もし修正し忘れるとテストが失敗するだろう。 -
//a[@id = 'btn-make-appointment' and (text() = 'Make Appointment' or . = 'Make Appointment')]
のようにid属性による選択と内容文字列による選択の二つをAND条件で結合する必要はない。どちらか片方あれば十分だ。
こう考えれば次の2つのうちいづれかを選択することになるでしょう。
//a[@id = 'btn-make-appointment']
//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式が更新されるのが分かります。
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機能によって自動されていました。
わたしが予約ページで受診日を入力しようとtextフィールドにマウスをのせたら、JavaScriptが動いてDate Chooserの小窓の中から日付を選べと促してきた。そこでわたしは30日をクリックしました。その操作がrecordされた結果として td_30
が生成されたのでした。
じつは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つのページで構成されていてボタンをクリックすることによりページからページへ遷移します。
この図には5本の矢印があります。矢印はページ間の遷移を表しています。ボタンのクリックによってねらいどおりのページに遷移できたかどうかを確かめたい。どうやって確かめるか?
遷移先のページの中に存在しているはずのHTML要素がいま現在表示中のページの中にあるかどうかをTest Caseが調べればいい。 それによってページ遷移が成功したかどうかを判断することができます。
各ページを特徴付けるHTML要素を特定します。たとえばホーム・ページには ボタンがある。そこで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)
このコードは次のことをしています。
- ブラウザを立ち上げる
- ブラウザで http://demoaut.katalon.com/ を開く
- ページのなかに
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
表示されたデータを検証しよう
確認画面に受信予約の内容が表示されます。
ここに表示されたデータが妥当であるかどうか?それを検証するコードを書きましょう。
どういう条件が満足されればデータが妥当だと判断するのか、その仕様を決める必要があります。ここでは下記の通りとします。
Test Object名 | データの検証条件 |
---|---|
p_facility | 次の3通りの文字列いづれかと一致することTokyo CURA Healthcare Center Hongkong CURA Healthcare Center Seoul CURA Healthcare Center
|
p_hospital_readmission |
Yes と No のいづれかと一致すること |
p_program |
Medicare と Medicaid と None のいづれかと一致すること |
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の画面の右下にログが表示されます。こんなふうに:
もしも FAILED があれば赤いバツ印が表示されます。こんなふうに:
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