ページオブジェクトパターンによる
⾃動テストメンテナンスの効率化
株式会社ネクスト HOMEʼS事業本部
サービス推進部 品質管理グループ
藤澤 正通
藤澤 正通
[email protected]
NEXT Co.,Ltd.⾃動化への取組み
2011年4⽉:リグレッションテストの⾃動化検討を開始
6⽉:Selenium IDEを採⽤、⾃動化に着⼿
7⽉:機能カバレッジ10%に到達
部分的な運⽤を開始
10⽉:機能カバレッジ50%に到達
リグレッションを数度検出し効果を実感
同時にメンテ⼯数の増加を感じ始める
12⽉:テスト対象システムの⼤規模な拡張
メンテナンスがほぼ毎⽇の作業となる
2012年1⽉:テスト対象システムがさらに拡張
Selenium IDEに限界を感じ始める
2012年2⽉:テスト対象システムがさらに拡張
Selenium IDEの破棄を決意
NEXT Co.,Ltd.Selenium IDEの良いところ
•
無料
•
簡単
レコ ド&プレイバック
–
レコード&プレイバック
•
テストシナリオ通りにブラウザを操作すれば、
⾃動的にテストが⽣成される
–
シンプルな⽂法
•
⽂法がシンプルなので、コードを⼿で記述する場合でも簡単
例:物件を検索 → 検索後のページタイトルを検証する場合 open http://www.homes.co.jp/search/ type //input[@id='freeword_input'] “海が⾒える物件“ clickAndWait //input[@value='検索'] verifyTitle “海が⾒える物件”の検索結果Selenium IDEの今⼀歩なところ
•
⾮常にシンプルなスクリプト⾔語なので、
次のような処理はできない
–
変数の定義と利⽤
–
変数の定義と利⽤
–
関数の定義と利⽤
–
条件分岐(if ...else...)
–
繰り返し処理(loop/iteration)
※ 厳密には可能なのですが可読性が悪く、使いこなすのは困難でした
その結果、テストスクリプトには同じような記述
が何度も登場することになる!
NEXT Co.,Ltd.Selenium IDEの課題(具体例)
•
例えば、
不動産情報の検索システム
に関する次のようなテスト
シナリオがあるとする
1. 検索画⾯を開く
2. キーワードを⼊⼒
→ 検索ボタンをクリック
3. 検索結果ページのタイトル
に検索キーワードが含まれ
ることを確認
4. キーワードを変更
→ 検索ボタンをクリック
検索ボタンをクリック
5. 検索結果ページのタイトル
に新しい検索キーワードが
含まれることを確認
これをSelenium IDEの
スクリプトで表現すると…
t
//i
t[@id ʻk
dʼ]
⽬⿊ ペット可
Selenium IDEの課題(具体例)
1. 検索画⾯を開く
2. キーワードを⼊⼒ → 検索ボタンをクリック
open
http://www.homes.co.jp/serarch/
type
//input[@id=ʻkeywordʼ]
23区 ペット可
verifyTitle
⽬⿊ ペット可の物件検索結果
type
//input[@id=ʻkeywordʼ]
⽬⿊ ペット可
clickAndWait //input[@id=ʻsubmitʼ]
3. 検索結果ページのタイトルに検索キーワードが含まれることを
確認
4. キーワードを変更 → 検索ボタンをクリック
yp
p [
y
]
clickAndWait //input[@id=ʻsubmitʼ]
5. 検索結果ページのタイトルに新しい検索キーワードが含まれる
ことを確認
verifyTitle
23区 ペット可の物件検索結果
NEXT Co.,Ltd.t
//i
t[@id ʻk
dʼ]
⽬⿊ ペット可
Selenium IDEの課題(具体例)
1. 検索画⾯を開く
2. キーワードを⼊⼒ → 検索ボタンをクリック
open
http://www.homes.co.jp/serarch/
type
//input[@id=ʻkeywordʼ]
23区 ペット可
verifyTitle
⽬⿊ ペット可の物件検索結果
type
//input[@id=ʻkeywordʼ]
⽬⿊ ペット可
clickAndWait //input[@id=ʻsubmitʼ]
3. 検索結果ページのタイトルに検索キーワードが含まれることを
確認
4. キーワードを変更 → 検索ボタンをクリック
同じ要素指定の
繰り返し
yp
p [
y
]
clickAndWait //input[@id=ʻsubmitʼ]
5. 検索結果ページのタイトルに新しい検索キーワードが含まれる
ことを確認
verifyTitle
23区 ペット可の物件検索結果
Selenium IDEの課題
この例のように
•
Selenium IDEで作成したスクリプトには重複した
記述が多くなる(同じ要素を複数の⾏で指定)
記述が多くなる(同じ要素を複数の⾏で指定)
•
重複は複数のファイルに及ぶ
その結果
•
システムのUIが1箇所変更されただけで、
テストスクリプトは何箇所も修正が必要になる
テストスクリプトは何箇所も修正が必要になる
•
テストケース数が少ないうちは
問題ないが、
テストケース数が増えてくると
、メンテナンス性が
著しく低下する
NEXT Co.,Ltd.解決策
ページオブジェクトパターン導⼊による効率化
Page Object Pattern
(
ページオブジェクトパターン
)
•
Page Object Patternとは
「テストシナリオを記述」するスクリプトと、
「実際にページを操作」するスクリプトを分離し
「実際にペ ジを操作」するスクリプトを分離し
シナリオは常にページオブジェクトを経由して
テスト対象を操作、検証する設計パターン
ページオブジェクト ---シナリオからの指⽰ を受け取り ページ シナリオ ---テストシナリオを記 述するスクリプト。 を受け取り、ペ ジ テスト対象ペ ジ の操作とページが持 つ情報の取得を⾏う 述するスクリプト。 テスト対象ページを 直接は操作せず、 ページオブジェクト に対して指⽰を⾏う テスト対象ページ NEXT Co.,Ltd.Page Object Patternの具体例
•
使⽤するツールと⾔語
–
Selenium 2(WebDriver)
•
SeleniumをAPIとして様々なプログラミング⾔語から
SeleniumをAPIとして様々なプログラミング⾔語から
呼び出すことが可能なフレームワーク
–
利⽤可能な⾔語
•
Java
•
C#
•
Ruby
•
Pythonなど
y
※このスライドでは例としてPythonを使⽤しています。
Page Object Patternの具体例
•
STEP1: Selenium IDEのコードをプログラミング
⾔語で書きなおす
※最初からIDEを使ってない場合はもちろん不要。 #scenario1.py #検索ページを開く driver.get(ʻhttp://www.homes.co.jp/search/ʼ) #検索キーワード⼊⼒テキストボックスを取得し、⽂字列をセット search_text_field = driver.find_element_by_xpath(//input[@id='keyword']) search_text_field.send_keys('⽬⿊ ペット可') #[検索]ボタンを取得し、ボタンをクリック search_btn = driver.find_element_by_xpath(//input[@id='submit']) search_btn.click() #検索結果ページのページタイトルを検証 assert(driver.title == ʻ⽬⿊ ペット可の検索結果ʼ) NEXT Co.,Ltd.Page Object Patternの具体例
•
STEP2: 要素を取得する処理を別のファイルに
抜き出す。
検索 ボ 得 #scenario1.py d i t(ʻhtt // h j / h/ʼ) #検索ペ ジを開く #検索キーワード⼊⼒テキストボックスを取得 search_text_field = driver.find_element_by_xpath(//input[@id='keyword']) #[検索]ボタンを取得 search_btn = driver.find_element_by_xpath(//input[@id='submit']) driver.get(ʻhttp://www.homes.co.jp/search/ʼ) #検索ページを開く search_text_field.send_keys(ʻ⽬⿊ ペット可ʼ) #検索キーワードをセット search_btn.click() #[検索] ボタンをクリック assert(driver.title == ʻ⽬⿊ ペット可の検索結果ʼ) #結果ページのタイトルを検証Page Object Patternの具体例
•
STEP3: 抜き出したファイルに名前を付け、
それをシナリオファイルにインポートして完成
#search page.py
#scenario1.py
import search_page as page _p g py #検索キーワード⼊⼒テキストボックスを取得 search_text_field = driver.find_element_by_xpath(//input[@id='keyword']) #[検索]ボタンを取得 search_btn = driver.find_element_by_xpath(//input[@id='submit']) p p g p g driver.get(ʻhttp://www.homes.co.jp/search/ʼ) #検索ページを開く page.search_text_field.send_keys(ʻ⽬⿊ ペット可ʼ) #検索キーワードをセット page.search_btn.click() #[検索] ボタンをクリック assert(driver.title == ʻ⽬⿊ ペット可の検索結果ʼ) #結果ページのタイトルを検証 NEXT Co.,Ltd.
Page Object Patternの具体例
•
このように要素の取得を⾏うファイルをページ毎
に作成・利⽤することで、シナリオファイルには
純粋なシナリオとしての記述のみが残る
純粋なシナリオとしての記述のみが残る
#search_page.py #実際にはこのファイルをページ毎にクラスとして作成します #検索キーワード⼊⼒ボックス search_text_field = driver.find_element_by_xpath(//input[@id='keyword']) #[検索]ボタン search_btn = driver.find_element_by_xpath(//input[@id='submit']) #scenario2.pyimport search_page as page
driver.get(ʻhttp://www.homes.co.jp/search/ʼ) #検索ページを開く page.search_text_field.send_keys(ʼ海が⾒える物件ʼ) #検索キーワードをセット page.search_btn.click() #[検索] ボタンをクリック assert(driver.title == ʼ海が⾒える物件の検索結果ʼ) #結果ページタイトルを検証
Page Object Patternの具体例
•
適⽤前
open http://www.homes.co.jp/ type //input[@id=ʻkeywordʼ] ⽬⿊ ペット可•
適⽤後
#scenario1.pyimport search page as page
clickAndWait //input[@id=ʻsubmitʼ] verifyTitle ⽬⿊ ペット可の検索結果
import search_page as page
driver.get(ʻhttp://www.homes.co.jp/search/ʼ) #検索ページを開く page.search_text_field.send_keys(ʻ⽬⿊ ペット可ʼ) #検索キーワードをセット page.search_btn.click() #[検索] ボタンをクリック assert(driver.title == ʻ⽬⿊ ペット可の検索結果ʼ) #結果ページのタイトルを検証
NEXT Co.,Ltd.