RSpec でテストをしていて、非表示にしてある要素が取れずにハマった。
Capybara さんはデフォルトでは画面上に見える要素のみを検索するので display: none や hidden になっている要素を触るできないっぽい。
<div class="modal" style="display:none"></div> <input type="hidden" id="secret_val" value="XXX">
scenario 'モーダルが存在する' do expect(page).to have_selector('.modal') end scenario '#secret_val の値は XXX' do expect( find('#secret_val').value ).to eq 'XXX' end
画面に見えてない要素は触れないのでテストが通らない
visible: false オプションを使う
scenario 'モーダルが存在する' do expect(page).to have_selector('.modal', visible: false) end scenario '#secret_val の値は XXX' do expect( find('#secret_val', visible: false).value ).to eq 'XXX' end
👉 success
オプションを付ければ非表示要素も検索してくれるのでテストが可能になる!
Options Hash (**options):
visible (Boolean, Symbol) — Only find elements with the specified visibility:
true- only finds visible elements.false- finds invisible and visible elements.:all- same as false; finds visible and invisible elements.:hidden- only finds invisible elements.:visible- same as true; only finds visible elements.cf. Module: Capybara::Node::Finders — Documentation for jnicklas/capybara (master)
非表示になっている要素のコンテンツは have_content / have_text で取れない
例えばデフォルトで非表示になっているモーダルのタイトルの内容があっているかテストしたいような場合
<div class="modal" style="display:none"> <p class="modal_title">アイカツを見ろ</p> </div>
scenario '正しいモーダルが出力されていること' do expect( find('.modal', visible: false) ).to have_content 'アイカツを見ろ' end
👇
Failure/Error: expect( find('.modal', visible: false) ).to have_content 'アイカツを見ろ' expected to find text "アイカツを見ろ" in "". (However, it was found 1 time including non-visible text.)
非表示の要素は取れていても have_content や have_text は 空 ("") と判断されてテストが落ちてしまう
have_selector で text: オプションも使ってマッチさせることでコンテンツ内容のテストができる
但し、非表示要素内の要素は have_selector でも visible オプションが必要
非表示なコンテナ内にあって、直接 display: none などのスタイルを当てられててない要素でも visible オプションがないと要素を取得できずにテストが落ちてしまう
scenario '正しいモーダルが出力されていること' do expect( find('.modal', visible: false) ).to have_selector('.modal_title', text: 'アイカツを見ろ') end
👇
Failure/Error: expect( find('.modal', visible: false) ).to have_selector('.modal_title', text: 'アイカツを見ろ') expected to find visible css ".modal_title" with text "アイカツを見ろ" within #<Capybara::Node::Element tag="div" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[2]/DIV[1]"> but there were no matches. Also found "", which matched the selector but not all filters.
have_selector にも visible オプションを付ける
scenario '正しいモーダルが出力されていること' do expect( find('.modal', visible: false) ).to have_selector('.modal_title', visible: false, text: 'アイカツを見ろ') end
👉 success
これならテストが通る!
ただ感覚的に「◯◯のテキスト が 'XXXX'」となってる方が直感的なわかり易さもテストの構造としても良い気がするので微妙な感じ…
.text(:all) で非表示になっているテキストノードを取得できる
#text(type = nil, normalize_ws: false) ⇒ String
Retrieve the text of the element. If ignore_hidden_elements istrue, which it is by default, then this will return only text which is visible. The exact semantics of this may differ between drivers, but generally any text within elements withdisplay:noneis ignored. This behaviour can be overridden by passing:allto this method.
cf. Method: Capybara::Node::Element#text — Documentation for jnicklas/capybara (master)
text(:all) でテキストを取得したほうが「◯◯のテキスト が 'XXXX'」と書けるので非表示になっている要素の内容のテストには良さそう
<div class="modal" style="display:none"> <p class="modal_title">アイカツを見ろ</p> <div class="modal_body"> <strong>アイカツオンパレード始まったから絶対見て!!!!</strong> </div> </div>
scenario '正しいモーダルが出力されていること' do # 文字列に含まれるをテストするので include を使う expect( find('.modal', visible: false).text(:all) ).to include 'アイカツを見ろ' end
👉 success
テストが通りました ٩(ˊᗜˋ*)و
非表示の要素内のテキストは find() に visible オプション・ text() に :all 引数の両方がないとダメ
text に :all がないと空("")になるのでテストが通らない
scenario '正しいモーダルが出力されていること' do expect( find('.modal', visible: false).text ).to include 'アイカツを見ろ' end
👇
Failure/Error: expect( find('.modal', visible: false).text ).to include 'アイカツを見ろ' expected "" to include "アイカツを見ろ"
find() に visible オプションがないとそもそも要素を取得できないのでテキストも取得できない
scenario '正しいモーダルが出力されていること' do expect( find('.modal').text(:all) ).to include 'アイカツを見ろ' end
👇
Failure/Error: expect( find('.modal').text(:all) ).to include 'アイカツを見ろ' Capybara::ElementNotFound: Unable to find visible css ".modal"
text の引数はこんな感じになっているそうです
見えないテキスト(display:none)が text メソッドの戻り値に含まれるかどうかについて
2.1 では、textメソッドの引数によって挙動を切り替えることもできるfind("#thing").text # Capybara.ignore_hidden_elements によって挙動が変わる find("#thing").text(:all) # 見えないテキストも含む find("#thing").text(:visible) # 見えるテキストだけcf. capybara 2.1 を学ぶ - おもしろwebサービス開発日記
感想
- 非表示になっている要素には
visible: falseオプションを使う - 非表示になっている要素の内容をテストするには
find(selector, visible: false).text(:all)を使う
のが良さそうです。
RSpec も Capybara も何も分からん… (docker-mac でのテスト走らせるのが遅くて辛いからどうにかなってほしぃ…
[参考]
- 非表示要素はCapybaraのfindで検索対象になるのか - Qiita
- visibilityのcapybaraテスト - Qiita
- Module: Capybara::Node::Finders — Documentation for jnicklas/capybara (master)
- capybara 2.1 を学ぶ - おもしろwebサービス開発日記
- Method: Capybara::Node::Element#text — Documentation for jnicklas/capybara (master)
- `include` matcher - Built in matchers - RSpec Expectations - RSpec - Relish

- 出版社/メーカー: バンダイ(BANDAI)
- 発売日: 2015/05/17
- メディア: おもちゃ&ホビー
- この商品を含むブログを見る