Capybara
selenium-webdriver
chromedriver

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 テストは経験上なんやかんやでこけるときがあるので特にエラーを限定しなくて良いのかなと判断した。