2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails+M1 Mac+Docker環境にSystem Specを導入する

Last updated at Posted at 2024-02-01

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 のエラーが発生するため削除します。

Gemfile
group :test do
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # gem 'webdrivers' ← これは削除する
end

その後コンテナ内で bundle install を実行します。

terminal
bundle install -j4

Selenium コンテナ定義の追加

selenium を起動するためのselenium_chromeコンテナを追加します。
注意点として、Apple シリコン 環境で動かす場合のコンテナイメージはselenium/standalone-chromeではなくseleniarm/standalone-chromiumという arm 環境に対応したイメージを指定してください。

docker-compose.yml
  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 コマンドを実行します。

terminal
rails generate rspec:install

するとspec_helper.rbrails_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/rails_helper.rb
# spec/supportにcapybara用の設定ファイルを配置するのでコメントアウトを外す
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

次にspec/support/capybara.rbを作成し、以下内容とします。
大まかな流れとして、Capybara.register_driver :remote_chromeremote_chromeという名前のドライバーを定義して、config.before(:each, type: :system, js: true)で System Spec 実行の際にremote_chromeドライバーを読み込むように定義しています。

spec/support/capybara.rb
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を作成し、以下定義とします。

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に静的ファイルをあらかじめコンパイルする必要があるので、コンテナ上で以下コマンドを実行し、静的ファイルの手動コンパイルを行います。

terminal
RAILS_ENV=test bundle exec rake assets:precompile

以上で準備は全て整ったので、最後に System Spec を実行します。コンテナ上で以下コマンドを実行します。

terminal
bundle exec rspec spec/system/static_pages_spec.rb

以下のように1 example, 0 failuresとなれば OK です。

terminal
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 形式で保存すると捗るかもしれません

spec/system/static_pages_spec.rb
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」エラーが発生する

これについては以下記事を参考にして、設定のチューニングを行って解消しました。

メモリ省力化設定を入れて「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コマンドで手動コンパイルを行い解消しました。

参考になった記事

いつも皆様のアウトプットに助けられております。ありがとうございます

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?