はじめに
Docker環境でRailsのWebアプリケーションを開発し始めた際、RSpecのSystem Specを実行するために必要な設定の全貌がなかなか分からず。
大ハマリしたので、手順を残しておきます。
Gemfile.lock
のバージョン情報:
開発には VSCode Remote Container を利用しています。
デフォルトで入っている 'webdrivers' gem は削除する
webrdrivers を利用すると、Docker環境ではなくローカル(=ホスト)側にブラウザを探しにいってしまうようで、Webdrivers::BrowserNotFound
のエラーが発生します。
Gemfile
:
# test以前は省略 group :test do gem 'capybara' gem 'launchy' gem 'selenium-webdriver' # gem 'webdrivers' ← これは削除する end
webrdrivers
のgemが読み込まれると発生してしまうエラー:
# gem 'webdrivers' を削除しないで bundle exec rspec を実行 Failure/Error: driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch('SELENIUM_DRIVER_URL'), } Webdrivers::BrowserNotFound: Failed to determine Chrome binary location.
参考:
補足
参考リンク先ではgem 'webdrivers', require: !ENV['SELENIUM_REMOTE_URL']
という修正方法になっています。
Docker環境とローカル環境の両方で動かせるようにしたいという事情がある場合、gem 'webdrivers', require: !ENV['SELENIUM_DRIVER_URL']
にしておくことで、「環境変数を設定しているコンテナで利用する場合はwebdrivers
を読み込まない」という形にできるからのようです。
自分の場合はDocker環境で使えればOKなので、gem 'webdrivers'
の行ごと削除しています。
ちなみに、そもそもGemfileのrequire ~~~
が何をしているのかについては、こちらの記事が分かりやすかったです。
Chromeのコンテナと、Railsのコンテナをネットワークで接続する
docker-compose.yml
を修正する必要があります。
下記のように、app
のコンテナとselenium_chrome
のコンテナを、同じネットワークで指定します。
version: '3' services: app: build: context: .. dockerfile: .devcontainer/Dockerfile volumes: - ../..:/workspaces:cached ### 追記 ### networks: ktg-net: selenium_chrome: image: selenium/standalone-chrome-debug ### 追記 ### networks: ktg-net: db: image: postgres:15.1 # 中略 ### 追記 ### networks: ktg-net: volumes: postgres-data:
ネットワーク接続ができていない状態だと、下記のエラーが発生しました。
Failure/Error: visit root_path SocketError: Failed to open TCP connection to selenium_chrome:4444 (getaddrinfo: Name or service not known)
利用するドライバーを設定する
RSpecの設定ファイル(/spec/rails_helper.rb
など)に設定を追加する必要があります。
自分の場合は設定を下記のようにspec/support/
配下に切り出しているため、spec/support/capybara.rb
のファイルにドライバーの設定を書きました。
# /spec/rails_helper.rb Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
spec/support/capybara.rb
:
# frozen_string_literal: true RSpec.configure do |config| config.before(:each, type: :system) do driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch('SELENIUM_DRIVER_URL') } Capybara.server_host = 'app' end end
以下、設定内容について解説します。
driven_by :selenium, using: :headless_chrome
ドライバの種類として:selenium
を指定し、利用するブラウザに:headless_chrome
を指定しています。
(利用するコンテナイメージも合わせて変更すれば、ブラウザには:headless_firefox
などを指定することも可能です。)
driven_by
のドキュメント:
なお、driven_by
でドライバとして設定できるのは、現時点で:selenium
, :poltergeist
, :webkit
, :cuprite
, :rack_test
の5種類のようです。
github.com
options の設定
options: { browser: :remote, url: ENV.fetch('SELENIUM_DRIVER_URL') }
ローカルではなくリモート環境のブラウザを利用するため、browser: :remote
を指定しています。
また、リモートに存在するドライバーのURLを指定する必要があるため、ここではdocker-compose.yml
で環境変数として設定したものを参照しています。
(環境変数に設定せずurl: 'http://selenium_chrome:4444/wd/hub'
と直接書いても問題なく動きます。)
docer-compose.yml
を修正し、appコンテナに環境変数を追加:
version: '3' services: app: build: context: .. dockerfile: .devcontainer/Dockerfile # 中略 ### 追記 ### environment: - SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub # 以下略
Capybara.server_host
テストアプリケーションが外部ホストからアクセスされる場合に設定が必要とのこと。
server_host (String = "127.0.0.1") - The IP address Capybara will bind the application server to. If the test application is to be accessed from an external host, you will want to change this to "0.0.0.0" or to a more specific IP address that your test client can reach.
Module: Capybara — Documentation for capybara (3.38.0)
デフォルト値が127.0.0.1
のため、何も設定しないとドライバがアプリケーションを見つけられず、エラーが発生します。
# Capybara.server_host の設定をせずに bundle exec rspec を実行 Failure/Error: visit root_path Selenium::WebDriver::Error::UnknownError: unknown error: net::ERR_CONNECTION_REFUSED (Session info: headless chrome=94.0.4606.61)