SlideShare a Scribd company logo
1 of 10
Download to read offline
Rails,RSpec & Capybara
で困った話
Akio Tajima (arton)
前提
• ソースレベルで調べたのもあるし、帰納的にこうだろうと推測
して対応したものもあるので、「いや、それは違う」があれば
教えてください!
• Ruby 2.6
• Rails 5.2
• CapybaraはChromeDriver+Selenium::WebDriver
コントローラ修正でハングアップ(dev)
• ソース追っかけたけどわかってない
• renderで他のコントローラを呼び出す場合に発生しているよう
に見える ex) render_to_string ‘他のコントローラ/アクション’
• リクエストによってコントローラが再ロードされた場合に、ク
ラスロードのロックがかかって他のコントローラをロードでき
なくなるのではと疑っている。
• ワークアラウンド: 内部で他のコントローラを呼ばないリクエ
ストを最初に呼び出す
JavaScriptの例外の検出
• spec/system/system_helperに以下のメソッドを登録して利用(stackoverflow)
• 元のGemfileがgem ‘capybara’, ‘~> 2.13’だったので、その時点の最新の3.32に修正が必要だった。合わせてChromeDriverの
更新も必要だった。
def no_js_error
logs = page.driver.browser.manage.logs.get(:browser)
logs.each do |log|
if log.level == 'SEVERE' && ['Uncaught', 'exception'].any? {|key| log.message.include?(key) }
logs.each do |log|
Rails.logger.debug "!!!! JavaScript error: #{log.level}: #{Time.at(log.timestamp / 1000)}: #{log.message}"
end
return log.message
end
end
nil
end
$が見つからないエラーでsystemスペック全滅
• 前回のrspec実行時のchromeが居残っている
• 親プロセスが1のchromeを殺す
Capybaraのfill_inやsetの取りこぼし
• 検索するとイッシューも見つかるし困っている人もいるが未解決
• タイミング(ChromeDriverとのインターフェイス時)依存のような
ので、あきらめる。
def exactly_set(selector, val)
until page.find(selector).value == val
page.find(selector, wait: 1).set(val)
sleep 0.1
end
end
clickのクリックミスは困る(ダブルクリックになると別の問題)
まともなCapybara用ドライバーが欲しい
ね
リクエスト―レスポンスベース時代であればよいが、
DOMのレベルでダイアログを表示したり非表示したりするスタ
イルと、非同期でChromeDriverなどにリクエストを出して結果
を見ずに返って来るCapybara(のドライバー)の仕組みが合っ
ていない。
https://medium.com/@yuliaoletskaya/capybara-inconsistent-
click-behavior-and-flickering-tests-f50b5fae8ab2
(直接JSでonclickを呼び出すソリューション 2018年)
DOMを操作する場合はとにかくexpect
page.find(selector1).click # selector2の要素が表示されるはず
page.find(selector2)…
でたまにselector2が見つからないエラーになる。
findは見つからなければエラーだがexpectは見つかるまである程度待
機するのでまずexpectを入れる
Capybara.default_max_wait_time = 5 # デフォルトは2
…
page.find(selector1).click
expect(page).to have_css(selector2)
page.find(selector2)…
expectにメッセージを追加
個別にexpectすれば行番号から問題個所はわかるがそうは言って
も群で処理したい場合はある。
Rails.root.join(‘config/routes.rb’).each_line do |route|
if /¥A¥s*(¥w+)¥s+’([^’]+)’/ =~ route
method, path = $1, $2
if method == ‘get’
resp = http.get(“/#{path}”) unless path.start_with?(‘login’)
expect(resp.code).to eq(403), “#{path}がやばたにえん”
receive
呼出し回数目毎のスペック
expect_any_instance_of(obj).to receive(:method).twice do |obj, args…|
~
end.and_return(…)
ブロックを取るメソッドへの介入
receive(:method).with(/check/, /check/) do |arg1, arg2, &arg3|
arg3.call(…)
end

Rails,RSpec & Capybara で困った話