assert_selectの使い方
webアプリケーションは、結局のところ適切なHTML文書を返すサーバーなので、最終的にはレスポンスとして返すHTMLが予想通りであれば良いということになる。ところが、HTMLを比較する時には単純に文字列として比較するのは問題がある。ブラウザの描画には影響しないコメントや改行、スペース、タブが含まれているからだ。確認したいのはwebブラウザの描画結果が適切なこと、つまりHTMLの構造が適切かどうか。
その確認をするために、Railsには「assert_select」メソッドが用意されていた。assert_selecteを利用すると、CSSのセレクタで指定する要領でHTMLの構造が適切かどうか、効率よく調べることができる。assert_selectは幅広い応用力を持っている!が、ちゃんと理解して使いこなすのはちょっと大変そうだ。以下は自分がひたすら試してみた、例題とその動き。
確認条件の例
# 存在 assert_select "form" # formタグが、1以上存在すれば成功。 assert_select "form", false # formタグが存在しなければ成功。 # 数 assert_select "form input", 4 # formタグ以下のinputタグの数が、4であれば成功。 # 範囲 assert_select "form input", 1..4 # formタグ以下のinputタグの数が、1〜4であれば成功。 # 文字列 assert_select "title", "Welcome" # titleタグの文字列が、「Welcome」と一致すれば成功。 # 正規表現 assert_select "title", /Welcome/ # titleタグの文字列に、「Welcome」が含まれていれば成功。 # ハッシュ assert_select "title", {:count=>5, :text=>"Welcome"} # titleの、タグ数が5 and テキストが「Welcome」であれば成功。 # ちなみに:countに代わって以下のシンボルも指定も可能。 assert_select "title", :minimum=>5 # titleタグ数が少なくても5個あれば成功。(5個以上なら成功) assert_select "title", :muximum=>5 # titleタグ数が最大5個までなら成功。(5個以下なら成功)
セレクタの例
タグ間の関係
# 「 」スペースは親と子以下の関係すべてを表す。 assert_select "body div.header" # 以下の構造が存在すれば成功。 # bodyとdivの関係は、親と子・孫・ひ孫...の差があれば階層は問わない。 # 「.」はclass属性を表す。 # <body> # ... # <div class="header"> # ... # </div> # ... # </body> # ちなみに上記の関係は、ブロックで表現することも可能。 assert_select "body" do assert_select "div.header" end # 「>」は親子関係を表す。 assert_select "ol > li#?", /item-\d+/ # 以下の構造が存在すれば成功。 # olとliの関係は、親子関係のみ。 # 「#」はid属性を表す。liのid属性は正規表現「/item-\d+/」にマッチする「item-数値」の形式である。 # <ol> # <li id="item-1"></li> # <li id="item-2"></li> # ... # <li id="item-10"></li> # ... # </ol> # ちなみに上記のHTMLの関係は、以下のようにも表現できる。 # 「~」は兄弟関係を表す。順序にも意味がある。 assert_select "li#item-1 ~ li#item-10" # 兄弟関係なので成功。 # 「+」は隣同士の兄弟関係を表す。順序にも意味がある。 assert_select "li#item-1 + li#item-2" # 隣同士なので成功。 assert_select "li#item-1 + li#item-10" # 離れているので失敗。 # 「,」で区切れば、複数のセレクタ条件を指定できる。 assert_select "li#item-0, li#item-1" # 少なくとも片方が存在するので成功。 assert_select "li#item-0, li#item" # どちらも存在しないので失敗。
タグの属性
assert_select "form input[name]" # formタグ以下に、name属性を持つinputタグが存在すれば成功。 # [属性名]で属性を表現できる。 assert_select "form input[name=slip]" # formタグ以下に、name="slip"属性を持つinputタグが存在すれば成功。 # 比較の記号文字によって、以下のような条件にもなる。 assert_select "form input[name^=slip]" # slipで始まる。 assert_select "form input[name$=slip]" # slipで終わる。 assert_select "form input[name*=slip]" # slipが含まれる。 assert_select "form input[name~=slip]" # スペース区切りの属性値の一つがslipである。 assert_select "form input[name|=slip]" # スペース区切りの属性値の先頭がslipである。 assert_select "form input[name=slip][id=slip-1]" # formタグ以下に、name="slip"属性とid="slip-1"属性の両方を持つinputタグが存在すれば成功。 # 複数の属性を指定可能。指定した全ての属性を持っていれば成功。 assert_select "form input[class=slip]" assert_select "form input.slip" # 上記は同等 assert_select "form input[id=slip]" assert_select "form input#slip" # 上記は同等 assert_select "form input[id^=slip]" assert_select "form input#?", /^slip/ # 上記は同等
タグの構成
assert_select "div p:only-child" # "div p"にマッチした<p>タグが、唯一の子なら成功。 assert_select "div p:first-child" # "div p"にマッチした<p>タグが、最初の子なら成功。 assert_select "div p:last-child" # "div p"にマッチした<p>タグが、最後の子なら成功。 assert_select "div p:nth-child(2)" # "div p"にマッチした<p>タグが、2番目の子なら成功。 assert_select "div p:nth-last-child(3)" # "div p"にマッチした<p>タグが、後ろから3番目の子なら成功。 assert_select "div p:only-of-type" # "div p"にマッチした<p>タグが、<p>タグとしては一つだけであれば成功。 assert_select "div p:first-of-type" # "div p"にマッチした<p>タグが、<p>タグとしては最初であれば成功。 assert_select "div p:last-of-type" # "div p"にマッチした<p>タグが、<p>タグとしては最後であれば成功。 assert_select "div p:nth-of-type(2)" # "div p"にマッチした<p>タグが、<p>タグとしては2番目であれば成功。 assert_select "div p:nth-last-of-type(3)" # "div p"にマッチした<p>タグが、<p>タグとしては後ろから3番目であれば成功。 # <div> # <p>test</p> # <ul> # <li>list1</li> # <li>list2</li> # </ul> # </div> # 上記HTMLをテストすると... assert_select "div p:only-child" # "div p"にマッチする<p>タグは、唯一の子でないから失敗。 assert_select "div p:only-of-type" # 子要素は2だが、<p>タグとしては1だけなので成功。 # <div> # <p> # <span></span> # </p> # </div> # 上記HTMLをテストすると... assert_select "div span:only-child" # "div span"にマッチする<span>タグは、唯一の子であるから成功。 assert_select "div>span:only-child" # "div>span"にマッチする<span>タグは、存在しないので失敗。 # <div> # <span id="test1"></span> # <p> # <span id="test2"></span> # </p> # </div> # 上記HTMLをテストすると... assert_select "div span#test1:only-child" # "div span#test1"にマッチする<span>タグは、唯一の子でないから失敗。 assert_select "div span#test2:only-child" # "div span#test2"にマッチする<span>タグは、唯一の子であるから成功。 assert_select "div span:only-child" # "div span"にマッチする<span>タグは、<span id="test2">がマッチして成功。 assert_select "div#flash:empty" # <div id="flash"></div>のようにタグの中身が空の状態なら成功。 assert_select "html:root" # <html>タグがそのページのルートであれば成功。
assert_select_rjs
書式
assert_select_rjs(id) assert_select_rjs(:method, id) assert_select_rjs(:insert, :position, id)
利用例
assert_select_rjs :remove, "j123456789" # xml_http_requestによって、id="j123456789"が削除されたら成功。 assert_select_rjs :replace, "journals_footer" do assert_select 'th', 3 assert_select "th input[value=1,000]", 1 end # xml_http_requestによって、id="journals_footer"の範囲で更新された内容についてブロック内の処理をする。 # 上記の対象が存在すれば成功。存在しなければ失敗。 assert_select_rjs :insert, :before, "journals_footer" do assert_select 'th', 2 assert_select 'td', 2 end # xml_http_requestによって、id="journals_footer"の範囲で:beforeの位置に:insertされた内容についてブロック内の処理をする。 # 上記の対象が存在すれば成功。存在しなければ失敗。
なんだかassert_select自体のテストを自動処理したい気分だ...。まだ、すべての機能を網羅できていない。
参考
以下のページがたいへん参考になりました。感謝です!