RailsのシステムテストでChromeを操作させるドライバのFerrumを使いやすくしたのがCuprite。 イメージとしてはNode.jsのPuppeteerに近くて、CDPプロトコルというものを利用しているのでSeleniumに比べて挙動が軽いのだとか。
インストールについて
実行環境にchromeのバイナリが必要。
Google Chromeをインストールしたくはなったので、Chromiumで代用する。
Ubuntu DesktopであればSeleniumはSnap経由でダウンロードできる。
Google Chromeは別途debパッケージをダウンロードする必要がある。
$ sudo snap install chromium
group :development, :test do
gem "capybara", "~> 3.39"
gem "cuprite", "~> 0.14.3"
gem "rspec-rails", "~> 6.0"
end
今回使うgemはこれ。
Systemテストのgemはわけもわからずたくさん入れてしまいがちだが、cupriteとcapybaraさえあれば動かせる。
TL;DR
Capybara.javascript_driver = :cuprite
# スクリーンショットの保存先を指定できる
Capybara.save_path = Rails.root.join("tmp/capybara/")
# ChromeのコンテナからRailsサーバーにアクセスするための設定
if ENV["CHROME_URL"].present?
ip_address = Socket.ip_address_list.find(&:ipv4_private?).ip_address
# app host
Capybara.app_host = "http://#{ip_address}"
# server host
Capybara.server_host = ip_address
end
# rspec settings
RSpec.configure do |config|
# 前回のスクリーンショットファイルは残さない
config.before(:all, type: :system) do
FileUtils.rm_rf Capybara.save_path
end
config.before(:each, type: :system) do
if ENV["CHROME_URL"].present?
# timeoutを設定するのが重要。GitLab CIがほぼ必ず落ちるのを回避できる。
options = {
browser_options: { 'no-sandbox': nil },
timeout: 20,
url: ENV["CHROME_URL"],
}
driven_by(:cuprite, screen_size: [800, 600], options: options)
elsif ENV["BROWSER"].present?
driven_by(:cuprite, screen_size: [800, 600])
else
driven_by(:rack_test)
end
end
end
Evil Martianの記事も参考にしながらサポートスクリプトを書いた。
とりあえずこれを入れれば動く。
よくあるtype: :system, js: trueみたいな書き方はしていない。
極力同じテストでJavaScriptありとなしの状態をテストできるようにしている。
ただし長い目で見ると破綻するかもしれないのでそこは注意が必要。
# rack_testのみのテスト
$ rspec
# システムのChromeとcupriteでテスト
$ BROWSER=1 rspec
# DockerとGitLab CIでテスト
$ docker-compose run --rm web rspec
ローカルで使うのはこの2種類。
基本的にはフットプリントの軽いrack_test前提でテストを書く。
CIで失敗したらBROWSER=1という環境変数でcupriteを有効にする。
version: "3"
services:
web:
build: .
environment:
- BROWSER=true
- CHROME_URL=http://chrome:3000
- RAILS_ENV=test
volumes:
- .:/web
depends_on:
- chrome
chrome:
image: browserless/chrome:latest
container_name: chrome
Dockerでは少なくともRubyのコンテナにブラウザをインストールする必要はないので、別のコンテナを利用する。 Browserlessがよさそうだ。 純粋なChromeのコンテナではなさそうだけれども、自分でコンテナをビルドする手間を省ける。
rspec:
image: "$CI_REGISTRY_IMAGE"
variables:
RAILS_ENV: test
script:
- bundle exec rails db:prepare
- bundle exec rspec
rspec system:
image: "$CI_REGISTRY_IMAGE"
variables:
CHROME_URL: http://browserless-chrome:3000
RAILS_ENV: test
services:
- browserless/chrome:latest
script:
- bundle exec rails db:prepare
- bundle exec rspec spec/system
GitLab側のファイルはこれだけ。
注意すべきなのはaliasを指定しないとホスト名がbrowserless-chromeになること。
あるいはbrowserless__chromeを指定しなければならない。
でもこれだけでSystemテストが動かせるのだからやっぱり便利だ。
Capybara-Screenshot
Ferrum/Cupriteとは直接関係ないがあると便利。 スクリーンショットだけではなくHTMLも書き出してくれる。
rack_testのデバッグだと修正箇所がわかりやすい。