Rails アプリに E2E テストを導入する(技術選定編)で E2E テストをする上での選択肢や各スタックについて理解しましたが、
今回は実際に E2E テストを実行する環境を構築して、簡単なテストの実行まで行いたいと思います。
環境
- OS: Ventura 13.5(22G74)
- CPU: Apple M1
- Ruby: 3.2.2
- Rails: 7.0.8
- Docker Desktop: 4.21.1
Gem のインストール
Gemfile に必要な gem を追加します。webrdrivers が存在すると、Webdrivers::BrowserNotFound のエラーが発生するため削除します。
group :test do
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# gem 'webdrivers' ← これは削除する
end
その後コンテナ内で bundle install を実行します。
bundle install -j4
Selenium コンテナ定義の追加
selenium を起動するためのselenium_chrome
コンテナを追加します。
注意点として、Apple シリコン 環境で動かす場合のコンテナイメージはselenium/standalone-chrome
ではなくseleniarm/standalone-chromium
という arm 環境に対応したイメージを指定してください。
rails: &rails
container_name: rails
build:
context: ./
dockerfile: Dockerfile
command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
stdin_open: true
tty: true
volumes:
- .:/app:delegated
- node_modules:/app/node_modules:delegated
- bundle:/usr/local/bundle:delegated
depends_on:
- mysql
- redis
environment:
DATABASE_HOST: mysql
REDIS_HOST: redis
WEBPACKER_DEV_SERVER_HOST: webpacker
NODE_OPTIONS: --openssl-legacy-provider
SELENIUM_DRIVER_URL: http://selenium_chrome:4444/wd/hub # <- 追加
CAPYBARA_SERVER_HOST: rails # <- 追加
CAPYBARA_APP_HOST: http://rails # <- 追加
ports:
- 3000:3000
webpacker:
<<: *rails
container_name: webpacker
command: bundle exec bin/webpack-dev-server
environment:
WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
NODE_OPTIONS: --openssl-legacy-provider
ports:
- 3035:3035
# 追加
selenium_chrome:
# seleniumだとApple Siliconで動かないので、代わりにseleniarmを使う
image: seleniarm/standalone-chromium:4.17.0
ports:
- '4444:4444'
Capybara / System Spec の設定
まずコンテナ内で rails generate rspec:install コマンドを実行します。
rails generate rspec:install
するとspec_helper.rb
とrails_helper.rb
が生成されますが、これらには以下のような役割の違いがあります。
-
spec_helper.rb
- Rails に限定しない Rspec の基本設定を行うファイル
-
rails_helper.rb
- Rails のテスト環境を設定するためのファイル。spec_helper.rb を require することで、RSpec の基本設定を継承する
今回は Rails の PJ なので基本的にrails_helper.rb
を編集することになります。
早速、rails_helper.rb を編集して、のちに作成する capybara 用の設定ファイルを読み込めるようにします。
# spec/supportにcapybara用の設定ファイルを配置するのでコメントアウトを外す
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
次にspec/support/capybara.rb
を作成し、以下内容とします。
大まかな流れとして、Capybara.register_driver :remote_chrome
でremote_chrome
という名前のドライバーを定義して、config.before(:each, type: :system, js: true)
で System Spec 実行の際にremote_chrome
ドライバーを読み込むように定義しています。
require 'capybara/rspec'
require 'selenium-webdriver'
# カスタムドライバーの定義
Capybara.register_driver :remote_chrome do |app|
url = ENV["SELENIUM_DRIVER_URL"]
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('no-sandbox')
options.add_argument('headless')
options.add_argument('disable-gpu')
options.add_argument('disable-dev-shm-usage')
options.add_argument('remote-debugging-port=9222')
options.add_argument('window-size=950, 800')
Capybara::Selenium::Driver.new(app,
browser: :remote,
url: url,
capabilities: options)
end
# System Specの設定
RSpec.configure do |config|
# System Specのテスト実行前に実行される共通定義(jsフラグが未指定の場合)
config.before(:each, type: :system) do
driven_by :rack_test
end
# System Specのテスト実行前に実行される共通定義(jsフラグをtrueとする場合)
config.before(:each, type: :system, js: true) do
# 定義したカスタムドライバーを使用する
driven_by :remote_chrome
Capybara.server_host = IPSocket.getaddress(Socket.gethostname)
Capybara.server_port = 4444
Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}"
end
end
System Spec 定義の作成
テスト自体の定義を作成します。今回はテストが正常に実行するかを確認したいので、「ルートパスに遷移し、トップページの h2 タグ内の文字列が想定通り表示されているか」というシンプルなテストケースを行うことにします。
spec/system/static_pages_spec.rb
を作成し、以下定義とします。
require 'rails_helper'
RSpec.describe 'Static Pages', type: :system, js: true do
it 'can render top page' do
visit root_path
expect(page).to have_selector('h2', text: '現役エンジニアから、')
expect(page).to have_selector('h2', text: 'キャリアをつくるスキルを学ぶ')
end
end
テストの実行
webpacker/webpack を利用している場合、通常の静的ファイルのコンパイルのパスpublic/packs
ではなくpublic/packs-test
に静的ファイルをあらかじめコンパイルする必要があるので、コンテナ上で以下コマンドを実行し、静的ファイルの手動コンパイルを行います。
RAILS_ENV=test bundle exec rake assets:precompile
以上で準備は全て整ったので、最後に System Spec を実行します。コンテナ上で以下コマンドを実行します。
bundle exec rspec spec/system/static_pages_spec.rb
以下のように1 example, 0 failures
となれば OK です。
root@2392cecb2a2e:/app# bundle exec rspec spec/system/static_pages_spec.rb
`Redis.current=` is deprecated and will be removed in 5.0. (called from: /app/config/initializers/redis.rb:2:in `<main>')
.
Finished in 11.81 seconds (files took 4.41 seconds to load)
1 example, 0 failures
root@2392cecb2a2e:/app#
以上で System Spec を導入し、実際にテストを実行することができました 🎉
ハマりどころと対応
作業する中でハマった点を 2 点ご紹介します
切り分けの際はsave_and_open_page
というメソッドを使い、特定時点の画面の状態を HTML 形式で保存すると捗るかもしれません
RSpec.describe 'Static Pages', type: :system, js: true do
it 'トップページ表示' do
visit root_path
save_and_open_page # <-この時点のHTMLが保存される
expect(page).to have_selector('h2', text: '現役エンジニアから、')
expect(page).to have_selector('h2', text: 'キャリアをつくるスキルを学ぶ')
end
end
「Selenium::WebDriver::Error::UnknownError: unknown error: session deleted because of page crash」エラーが発生する
これについては以下記事を参考にして、設定のチューニングを行って解消しました。
「Webpack が JavaScript ファイルを見つけられない」エラーが発生する
以下のようなエラーが発生するケースです。
ActionView::Template::Error:
Webpacker can't find products.js in /app/public/packs-test/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
}
これについてはテストの実行で触れた通り、public/packs-test に静的ファイルをあらかじめコンパイルする必要があるので、bin/webpack
コマンドで手動コンパイルを行い解消しました。
参考になった記事
いつも皆様のアウトプットに助けられております。ありがとうございます
-
Everyday Rails - RSpec による Rails テスト入門
- いかにして RSpec を使うかが実践的かつ体系的にまとまっており、Rails でテストを扱う人は読んでおいて損はないと思います。かの有名な Junichi Ito さん翻訳
-
Docker for Mac M1 で System Spec 沼にハマった話
- M1 の Docker 上で system spec を動かそうと思ったら動かなかった話
-
Apple silicon 搭載 Mac で SystemSpec を動かす
- Apple silicon 搭載 Mac の Docker 上で SystemSpec を動かす際のハマりどころと設定方法
-
M1 Mac の Docker で Chromium を使った Feature Spec を動かす
- M1 の Docker 上で Feature Spec が動かなくなった際の対応方法
-
M1 Mac で Rails + Docker + RSpec + Capybara での System テストの環境構築
- System Spec を実行するための設定について
-
Docker + Rails 7 + RSpec で System Spec を実行するための設定
- System Spec の環境構築と CI(GitHub Actions)への対応方法
-
【RSpec】before(:suite)/before(:all)/before(:each)それぞれの違いについてまとめてみた
- RSpec の before(:suite)/before(:all)/before(:each)の違いについての説明
-
メモリ省力化設定を入れて「Selenium::WebDriver::Error::UnknownError: unknown error: session deleted because of page crash」が出ないようにする
- Selenium が crash してしまう時の対応方法