いろいろ調査しながら対応したのでまとめておきます
環境
rails (6.0.1)
rspec-rails (3.8.2)
capybara (3.29.0)
docker 2.1.0.5 (40693)
手順
gemのインストール
まずはSystem Specに必要なgemをインストールします。
group :test do
gem 'capybara'
gem 'selenium-webdriver'
end
もちろん、Rails環境がDockerで動いている場合はコンテナ内で実行する必要があります。
# サービス名がappの場合
$ docker-compose run --rm app bundle install
Docker用スクリプト作成
System Specに必要なchromeをどう用意するかですが、
- chromeをrails環境と一緒のイメージでbuildする
- chrome用コンテナをdocker-composeに追加する
- chrome用コンテナをdockerコマンドで立ち上げる
などなど、いろいろ方法があって、今回は「既存のコンテナに変更を加えなくて良い」という理由で3にしました。
そして、いちいちdockerコマンドを叩くのも面倒なのでスクリプトを作りました。
spec実行時にchromeのdockerコンテナを起動して、spec終了時にコンテナも終了させています。
なお、Dockerイメージにはselenium/standalone-chrome
を使っています。
SeleniumHQ/docker-selenium
#!/bin/sh
docker run -itd --rm --net=your_app_default --name chrome -p 4444:4444 selenium/standalone-chrome
if [ $# -eq 0 ]; then
docker-compose run --rm app rspec spec/system
else
docker-compose run --rm app rspec $*
fi
docker stop chrome
ちなみに--net
のネットワーク名はディレクトリ名_default
となるので注意してください。
詳しくはreferenceを参照してください。
- Docker run リファレンス — Docker-docs-ja 17.06.Beta ドキュメント
- Compose のネットワーク機能 — Docker-docs-ja 17.06.Beta ドキュメント
以下のように実行します。
# すべてのsystem_specを実行
$ bin/system_spec
# 一部のsystem_specを実行
$ bin/system_spec spec/system/users_spec.rb
System Spec
gemもコンテナも用意できたので、specを書いていきます。
driverでのremote設定、app_host設定あたりがポイントです。
# frozen_string_literal: true
require 'capybara/rspec'
Capybara.register_driver :headless_chrome do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
args: %w[--headless --no-sandbox --disable--gpu --window-size=1280x800]
}
)
opts = { desired_capabilities: capabilities, browser: :remote, url: 'http://chrome:4444/wd/hub' }
Capybara::Selenium::Driver.new(app, opts)
end
Capybara.server_host = '0.0.0.0'
Capybara.javascript_driver = :headless_chrome
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :rack_test
end
config.before(:each, type: :system, js: true) do
driven_by :headless_chrome
Capybara.app_host = "http://#{Socket.gethostname}"
end
end
あとはこんなかんじでspecを書いていけばOKです。
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Users', type: :system, js: true do
describe 'ユーザー一覧' do
subject(:visit_page) { visit '/users' }
it do
visit_page
expect(page).to have_current_path '/users'
end
end
end
以上です