Selenium 中級者になろう (変数+XPath+JavaScriptを,テストケース中で利用する方法)
回帰テストツール「Selenium」の中級 Tips。
初級の使い方については
今から3分で selenium の使い方を身に付ける (回帰テスト自動化)
http://language-and-engineering.hatenablog.jp/entry/20081016/1224080409
selenium 主なコマンド一覧
http://language-and-engineering.hatenablog.jp/entry/20081016/1224123118
で入門のこと。
Seleniumのより便利な使い方として,下記で
- (1)変数の使い方
- (2)XPathの使い方
- (3)テストケース中へのjavascriptの埋め込み
- (4)Ajaxアプリのテスト方法
を学ぶ。
まず,まとめを掲載。そのあとで,実際のテストでどう役立つのか詳しく解説する。
まとめ
要素の指定方法まとめ
ElementLocator(要素を指定するための文字列)にはDOM ID,xpath, document.〜,javascript{"DOM"+"ID";}, ${varname}が使える。
- verifyElementPresent fuga
- verifyElementPresent //input[@id="fuga"]
- verifyElementPresent document.getElementById("fuga")
- verifyElementPresent document.getElementsByTagName("input")[0]
- verifyElementPresent javascript{ "fu" + "ga"; }
- store fuga elem_id
verifyElementPresent ${elem_id}- store fuga elem_id
verifyElementPresent javascript{ storedVars["elem_id"]; }
変数のまとめ
格納
- store 値 変数名
- storeValue 要素 変数名
- getEval storedVars["変数名"] = 値;
参照
- ${変数名}
- javascript{ storedVars["変数名"] }
JSのまとめ
埋め込み
- javascript{ コード }
- getEval コード
- verifyEval コード 値
JSコード中で利用できる特殊なオブジェクト
- this : selenium内の専用環境を指す
- this.page().getDocument() : テスト中のページのdocument
- this.page().getCurrentWindow() : テスト中のページのwindow
- storedVars : 変数格納用のハッシュ
- selenium : seleniumコアのいろいろ操作とかできるオブジェクト
以下解説。
(1)Seleniumで変数を使う
下記ではこんなHTMLを想定。
ユーザ名:<input type="text" id="input1"> あいさつ:<input type="text" id="input2">
変数をそのまま使う
store系のコマンドで保存。${}で変数呼び出し。
「input1」というIDのDOM要素に,「user1」と入力したい。
# 入力内容の文字列として,str_usernameという変数に格納したuser1という値を利用 store user1 str_username type input1 ${str_username} # 入力対象の要素IDとして,input_usernameという変数に格納したinput1という値を利用 store input1 input_username type ${input_username} user1
具体的なテストデータや要素IDは,テストケースの先頭で変数として宣言しておけば,いざという時に変更は一箇所ですむ。
つまり,テストケースの保守が容易になる。
なお,${varname}の部分はjavascript{ storedVars["varname"]; }としても同じ。
storedVarsは,変数を格納するための連想配列。
変数をJavaScriptで加工して使う
「input1」という要素のvalueに,名前が入っているとする。
これを「Hello, 〜!」のように加工してから,input2のvalueに入力したい。
storeValue input1 str_username type input2 javascript{ "Hello, "+storedVars["str_username"]+"!"; }
JavaScriptから変数を参照すれば,保管した値を好きなように加工できる。
「今日の日付」とか「乱数」とかを扱う場合,テストケース中で固定値を書くことは不可能(値を予測することができない)。
だからこのように
- Webページ上にその時点で表示されている値を見て,
- それを変数に格納して,
- 他の値と比較
という風にして,柔軟に対処できる。
(2)SeleniumでXPathを使う
シンプルなHTMLならば,DOM IDだけでテスト可能だ。
でも,動的に変化するようなWebページの場合,各要素にIDを振りづらい。それに
- 特定のCSSクラスであるような n 番目のdiv要素
- IDが「hoge_」で始まる全要素
みたいな指定はできない。
しかし,XPathなら可能。
jQueryのCSSセレクタのように,強力に要素選択できる。
→なので
- Webページが複雑な構造でもテストが怖くない
- テストのために全部IDを振りなおしてくれ,みたいな事を開発者・デザイナ・テスター間で調整して回らなくて済む。
というメリットがある。ただ,記法を覚える必要があるが。
XPathの基礎知識
独習に役立つようなWebページは非常に少ない。以下参考URL
XMLパス言語 (XPath) Version 1.0
http://www.doraneko.org/xml/xpath10/1...
仕様書。これだけ読んで理解するのは不可能
JavaScript-XPath をリリースしました!さあ、あなたも XPath を使おう!(解説付き)
http://d.hatena.ne.jp/amachang/200711...
チートシート有
XPath入門、実用例
http://d.hatena.ne.jp/javascripter/20...
- javascripter氏によるサンプルの列挙
XPathの学習方法としては,FireBugのコンソールでリアルタイムで確かめてみる,というのが最も良い方法だろう。
FireBugを開き,コンソールの一番下の「>>>」と書いてある行に
$x("//*")
と入力し,エンターキーを押下。すると,表示中のページの全要素が列挙される。
今度は
$x("//td/a[@href]")
とすれば,td要素の子要素であるようなaリンク要素がすべて列挙される。
$x( XPath記法 ) のようにして,
- その記法が実際にどの要素を現すか
- 正しい記法か
確かめられるというわけだ。
省略記法を覚えれば,要素指定は短くなる。
例えば,
- //table[@id="hoge"]/tbody/tr[1] と書く代わりに,
- //table[@id="hoge"]//tr[1] と書いてよい。
※//は「それ自身か,または子孫全体の中から選ぶ」という意味。
注意点として,ノードセット(ノードの配列)の指定は間違いやすい。
「現在のページ内に存在する2つめのリンク」を指定したい場合,
$x("//a[@href][2]")
ではだめ。「何かの親要素の2番目の子要素として存在しているa要素」がすべて列挙されてしまう。
かわりに
$x("descendant::a[@href][2]")
とすればOK。
XPathにおける//*とdescendant::*の違い
http://d.hatena.ne.jp/os0x/20080711/1...
Seleniumテストケース内で,Xpathで要素を指定する
- //
- xpath=
のいずれかで始めると,xpath記法として解釈される。
Seleniumでは,要素の存在判定のときにXPathが便利。
判定したい要素に特定のclassを持たせておいて,そういうクラスの要素が存在するかどうか確かめればよい。
#hogeというclassのspan要素が存在することを確認 assertElementPresent //span[@class="hoge"] #hogeというclassのspan要素が【2個】存在することを確認 verifyElementPresent xpath=descendant::span[@class="hoge"][1] verifyElementPresent xpath=descendant::span[@class="hoge"][2]
つまり,XPathを利用して,getElementsByClassNameしている。
メッセージや文言の表示確認などで便利。
(3)要素や値の指定以外にも,JavaScriptを利用してみよう
(1)で既に見たとおり,javascript{〜}によって要素IDとか値とかを指定できる。
それ以外の方法でも,JSコードを埋め込み可能。
Seleniumのテストケース中で,任意のJavaScriptコードを実行したい
# hogeとアラート表示 getEval alert("hoge"); # ページ内任意要素のinnerHTMLをアラート表示 getEval alert( this.page().getDocument().getElementById("div_content").innerHTML );
特に後者は,テストの途中でページ内の状況を確かめることができて便利。
Selenium内JavaScript実行用コマンド「getEval」
http://colo-ri.jp/develop/2008/04/sel...
なお,
- this.page().getDocument()は,テスト対象のページのdocumentを指す。
- 単なる「document」は,getEvalのようなJSコード中では,Seleniumのテストツールのある画面が取得されてしまう。
windowについても同じで,
- this.page().getCurrentWindow()は,テスト対象のページのwindowを指す。
- 単なる「window」は,Seleniumのテストツールのある画面を指してしまう。
よって,もしテスト対象のページ内で$()という関数が定義されていれば,this.page().getCurrentWindow().$()によって呼び出せる。
また,user-extensions.js中に関数を定義しておけば,テストケース内でその関数を呼び出すこともできる。
Seleniumに外部JavaScriptファイル(.js)を読み込んで関数を実行する
http://colo-ri.jp/develop/2008/04/sel...
しかし,これはあまり得策ではないだろう。テストツールに手を加えたら,テストのテストをしなきゃならないので・・・
値の高度な比較
verifyEvalとかassertEvalを使えば,JavaScriptを介して複雑なAssertionが実現できる。
たとえば下記のようなHTMLを想定:
きょうの日付は<span id="hoge">2009年1月1日</span> <span id="fuga">2009/1/1</span>の行動予定:〜〜
フォーマットは異なるけども,同じ値が入っていてほしい。
しかも,テストを実行する日付しだいで,実際に画面上に表示される日付も変わってしまう。
というような場合。
# まずspanの中身を変数に格納して storeText hoge str_date1 storeText fuga str_date2 # 互いに比較 verifyEval storedVars["str_date1"].match(/(.*)年(.*)月(.*)日/);RegExp.$1+"/"+RegExp.$2+"/"+RegExp.$3; ${str_date2}
hogeのほうを加工して,fugaに等しくなるかどうかをチェックしている。
なお,コードブロック内で最後に評価した値(ここではRegExp.$1+"/"+RegExp.$2+"/"+RegExp.$3)が,比較のために利用される。(Rubyっぽい)
もうちょっと簡単なサンプル:
# ページのタイトルを変数に保存してから store javascript{this.page().getDocument().title} fuga # 変数とページのタイトルを比較。(当然ながら互いに等しい) verifyEval this.page().getDocument().title ${fuga}
書き方にはいろいろ幅がある。
ある変数の中身について,文字列の長さを確かめたい場合は
# 数値と比較 verifyEval storedVars["hoge"].length 5 # 真偽値と比較 verifyEval storedVars["hoge"].length==5 true
のように,2通りの書き方ができる。
このようにSeleniumの世界にJavaScriptを持ち込むことができれば,もうあとはやりたい放題にテストできるはず。
(4)Ajaxアプリのテスト
div_ajaxという要素内に,AjaxでHTMLを読み込むとしよう。
その読み込みが終了するまで待つには,waitFor〜〜コマンドを使う。
# 要素の中に「読み込み完了」という文字列が含まれる状況になるのを待つ。10秒でタイムアウト。 waitForCondition var val=selenium.getText("div_ajax");val.match(/読み込み完了/)!=null; 10000
特定の要素が読み込みによって出現するのを待っても良い。
# 読み込みによってh3要素が現れるのを待つ
waitForElementPresent xpath=//h3[@class="content_loaded"] 10000
あるいは,ローディング中のクルクル回る画像が消えたのを,waitForElementNotPresent で検出してもOK。
SeleniumでAjaxアプリケーションをテストする
http://www.infoq.com/jp/articles/test...
何かをチェックためのverifyXxxxやassertXxxxがあるなら、非同期効果をテストするためのwaitForXxxxが必ず存在します。
以上のことを知っておけば,やりたいことはほぼSeleniumで実現できるだろう。
関連する記事:
"Excelenium"(エクセレニウム)で,快適な自動回帰テストを (Seleniumのテストスクリプトとテスト仕様書を自動生成)
http://language-and-engineering.hatenablog.jp/entry/20090524/p1
Androidアプリの自動テストツールで最も有望か - 「NativeDriver」,Google製「WebDriver」の拡張 (公式のAndroid版Selenium)
http://language-and-engineering.hatenablog.jp/entry/20110930/p1
IE AutoTester で,UIの回帰テストを完全自動化
http://language-and-engineering.hatenablog.jp/entry/20090922/p1