Edited at

Capybara + Selenium WebDriver による E2E テストで Net::ReadTimeout への対応(ヘッドレス Chrome の設定例)

More than 1 year has passed since last update.

Capybara + Selenium WebDriver による E2E テストを実行しているとき Net::ReadTimeout が発生することがある。

Net::ReadTimeout

--- Caused by: ---
IO::EAGAINWaitReadable:
Resource temporarily unavailable - read would block

これへの対応として、


  • (1) タイムアウトをのばす

  • (2) リトライする

  • (3) そもそもタイムアウトに引っ掛かるような処理を見直す

があると思う。(3) については各位がんばりましょうということで、ここでは (1) (2) について書く。


環境


  • Rails 4.2.5(がんばる...)

  • capybara 2.6.2

  • selenium-webdriver 3.8.0

  • Google Chrome 63.0.3239.84

  • ChromeDriver 2.34.522932


タイムアウトをのばす

https://github.com/SeleniumHQ/selenium/wiki/Ruby-Bindings#internal-timeouts のとおり

client = Selenium::WebDriver::Remote::Http::Default.new

client.read_timeout = 120 # seconds
driver = Selenium::WebDriver.for :remote, http_client: client

みたいなことをやれば良いのだけれど、これをみても、なるほどわからん、という人はいると思うので、もう少し周辺も含めた設定例を書いておく。


ヘッドレス Chrome を使った例

PhantomJS が最新の JS / CSS へ追随しなくなって、E2E テストで使うブラウザーを Chrome へ乗り換えた人も多いと思うので、ヘッドレス Chrome の設定例を書いておく。

# spec/support/capybara.rb

require 'capybara/rails'
require 'capybara/rspec'
require 'selenium-webdriver'

client = Selenium::WebDriver::Remote::Http::Default.new
client.read_timeout = 120 # instead of the default 60

Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
chrome_options: {
args: ['headless', 'window-size=1280,768'],
},
),
http_client: client,
)
end

Capybara.javascript_driver = :selenium


ポイント



  • timeout= メソッドは deprecated



    • client.read_timeout = xxx としている箇所、少し古いドキュメントだと client.timeout = xxx と書かれているものもあるけど、timeout= メソッドは deprecated になっている

    • Selenium の Wiki については 先ほど 中の人に直してもらえた(感謝)



  • ヘッドレス Chrome の disable-gpu オプションは不要



  • ヘッドレス Chrome を RSpec + Capybara で使うことについては下記が分かりやすい




リトライする

rspec-retry という便利な Gem があるのでオススメ。


設定例

使い方は公式の README に分かりやすく書いているので特に困らないと思うけれど、参考までに 転職ドラフト で使っている設定を載せておく。

# spec/support/retry.rb

require 'rspec/retry'

RSpec.configure do |config|
config.verbose_retry = true
config.display_try_failure_messages = true

# run retry only on features
config.around :each, :js do |example|
# 公式では 3 となっているけれど、日本には三度目の正直という言葉があるし、
# 3回やってもこけるテストは根本的に対応したほうが良いと考えた
example.run_with_retry retry: 2
end

# callback to be run between retries
config.retry_callback = proc do |example|
# run some additional clean up task - can be filtered by example metadata
Capybara.reset! if example.metadata[:js]
end
end


ポイント

config.exceptions_to_retry = [Net::ReadTimeout]

とやって、リトライする対象のエラーを指定する方法もある(詳細は公式 README 参照)が、JavaScript を利用した E2E テストは経験上なんやかんやでこけるときがあるので特にエラーを限定しなくて良いのかなと判断した。