SQiP シンポジウム 2012 ページオブジェクトパターンによる 動テストメンテナンスの効率化 株式会社ネクスト HOMEʼS事業本部 サービス推進部 品質管理グループ 藤澤 正通 FujisawaMasamichi@next-group.jp 1
動化への取組み 2011 年 4 : リグレッションテストの 動化検討を開始 6 :Selenium IDE を採 動化に着 7 : 機能カバレッジ 10% に到達部分的な運 を開始 10 : 機能カバレッジ 50% に到達リグレッションを数度検出し効果を実感同時にメンテ 数の増加を感じ始める 12 : テスト対象システムの 規模な拡張メンテナンスがほぼ毎 の作業となる 2012 年 1 : テスト対象システムがさらに拡張 Selenium IDE に限界を感じ始める 2012 年 2 : テスト対象システムがさらに拡張 Selenium IDE の破棄を決意 Selenium IDE の良いところ 無料 簡単 レコード & プレイバック テストシナリオ通りにブラウザを操作すれば 動的にテストが 成される シンプルな 法 法がシンプルなので コードを で記述する場合でも簡単 例 : 物件を検索 検索後のページタイトルを検証する場合 open http://www.homes.co.jp/search/ type //input[@id='freeword_input'] 海が える物件 clickandwait verifytitle //input[@value=' 検索 '] 海が える物件 の検索結果 2
Selenium IDE の今 歩なところ 常にシンプルなスクリプト 語なので 次のような処理はできない 変数の定義と利 関数の定義と利 条件分岐 (if...else...) 繰り返し処理 (loop/iteration) 厳密には可能なのですが可読性が悪く 使いこなすのは困難でした その結果 テストスクリプトには同じような記述が何度も登場することになる! Selenium IDE の課題 ( 具体例 ) 例えば 不動産情報の検索システムに関する次のようなテストシナリオがあるとする 1. 検索画 を開く 2. キーワードを 検索ボタンをクリック 3. 検索結果ページのタイトルに検索キーワードが含まれることを確認 4. キーワードを変更 検索ボタンをクリック 5. 検索結果ページのタイトルに新しい検索キーワードが含まれることを確認 これを Selenium IDE のスクリプトで表現すると 3
Selenium IDE の課題 ( 具体例 ) 1. 検索画 を開く open http://www.homes.co.jp/serarch/ 2. キーワードを 検索ボタンをクリック type //input[@id=ʻkeywordʼ] dʼ] ペット可 clickandwait //input[@id=ʻsubmitʼ] 3. 検索結果ページのタイトルに検索キーワードが含まれることを 確認 verifytitle ペット可の物件検索結果 4. キーワードを変更 検索ボタンをクリック type //input[@id=ʻkeywordʼ] 23 区ペット可 clickandwait //input[@id=ʻsubmitʼ] 5. 検索結果ページのタイトルに新しい検索キーワードが含まれる ことを確認 verifytitle 23 区ペット可の物件検索結果 Selenium IDE の課題 ( 具体例 ) 1. 検索画 を開く open http://www.homes.co.jp/serarch/ 2. キーワードを 検索ボタンをクリック type //input[@id=ʻkeywordʼ] dʼ] ペット可 clickandwait //input[@id=ʻsubmitʼ] 3. 検索結果ページのタイトルに検索キーワードが含まれることを同じ要素指定の確認繰り返し verifytitle ペット可の物件検索結果 4. キーワードを変更 検索ボタンをクリック type //input[@id=ʻkeywordʼ] 23 区ペット可 clickandwait //input[@id=ʻsubmitʼ] 5. 検索結果ページのタイトルに新しい検索キーワードが含まれることを確認 verifytitle 23 区ペット可の物件検索結果 4
Selenium IDE の課題 この例のように Selenium IDEで作成したスクリプトには重複した記述が多くなる ( 同じ要素を複数の で指定 ) 重複は複数のファイルに及ぶ その結果 システムのUIが1 箇所変更されただけで テストスクリプトは何箇所も修正が必要になる テストケース数が少ないうちは問題ないが テストケース数が増えてくると メンテナンス性が著しく低下する 解決策 ページオブジェクトパターン導 による効率化 5
Page Object Pattern( ページオブジェクトパターン ) Page Object Pattern とは テストシナリオを記述 するスクリプトと 実際にページを操作 するスクリプトを分離しシナリオは常にページオブジェクトを経由してテスト対象を操作 検証する設計パターン シナリオ ---------------------- テストシナリオを記述するスクリプト テスト対象ページを直接は操作せず ページオブジェクトに対して指 を う ページオブジェクト ---------------------- シナリオからの指 を受け取り ページの操作とページが持つ情報の取得を う テスト対象ページ 使 するツールと 語 Selenium 2(WebDriver) Selenium を API として様々なプログラミング 語から呼び出すことが可能なフレームワーク 利 可能な 語 Java C# Ruby Python など このスライドでは例として Python を使 しています 6
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 == ʻ ペット可の検索結果 ʼ) STEP2: 要素を取得する処理を別のファイルに抜き出す # 検索キーワード テキストボックスを取得 search_text_field = driver.find_element_by_xpath(//input[@id='keyword']) #[ 検索 ] ボタンを取得 search_btn = driver.find_element_by_xpath(//input[@id='submit']) #scenario1.py di driver.get(ʻhttp://www.homes.co.jp/search/ʼ) h / h/ʼ) # 検索ページを開く search_text_field.send_keys(ʻ ペット可 ʼ) search_btn.click() # 検索キーワードをセット #[ 検索 ] ボタンをクリック assert(driver.title == ʻ ペット可の検索結果 ʼ) # 結果ページのタイトルを検証 7
STEP3: 抜き出したファイルに名前を付け それをシナリオファイルにインポートして完成 #search_page.py py # 検索キーワード テキストボックスを取得 search_text_field = driver.find_element_by_xpath(//input[@id='keyword']) #[ 検索 ] ボタンを取得 search_btn = driver.find_element_by_xpath(//input[@id='submit']) #scenario1.py 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 == ʻ ペット可の検索結果 ʼ) # 結果ページのタイトルを検証 このように要素の取得を うファイルをページ毎に作成 利 することで シナリオファイルには純粋なシナリオとしての記述のみが残る #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.py 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 == ʼ 海が える物件の検索結果 ʼ) # 結果ページタイトルを検証 8
適 前 open http://www.homes.co.jp/ type //input[@id=ʻkeywordʼ] ペット可 clickandwait //input[@id=ʻsubmitʼ] verifytitle ペット可の検索結果 適 後 #scenario1.py 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 == ʻ ペット可の検索結果 ʼ) # 結果ページのタイトルを検証 Page Object Pattern の効果 可読性の向上 シナリオファイルから //input[@id=ʻsubmitʼ] のような直感的ではない記述がなくなる 代わりに search_text_field や search_btn のように 間が読んで理解できる名前になり 可読性が向上する シナリオ作成の効率化 すでにページオブジェクトを作成済みのページに対するテストシナリオを作成する場合は 純粋なシナリオの記述のみでケースを作成できるため 短時間でシナリオを量産できる シナリオで定形的に われる処理は関数化することで 再利 が可能になる 9
Page Object Pattern の効果 メンテナンス 数の削減 例えば 仕様変更によりアプリーションのGUIが変更され 検索ボタンの位置が //input[@id=ʻsubmitʼ] から //div[@id=ʻsend_qʼ]/img に変更された場合 ページオブジェクトを利 していなければ そのボタンを利 している全てのシナリオを修正しなければならない ページオブジェクトを利 していれば シナリオの数が何百あっても該当のページオブジェクトを 1 箇所のみ修正すれば良い まとめ Page Object Pattern を使うと コードの再利 が促進され 重複を排除できる テストが読みやすくなり 修正がしやすくなる メンテナンス性が 幅に向上するので 仕様変更が多いシステムのテストでは是 取り れるべき 10
ご清聴ありがとうございました 11