既存Railsアプリをdocker化したらRSpec走らん問題【初学者向け】

既存のrailsアプリをdocker化するとローカルでエラーなく走っていた`RSpec`の`system spec`のテストが何一つまともに走ってくれず、修正した内容の備忘録となります。
どなたかのお力になれば嬉しいです。

:::note warn
プログラミング初学者なので、内容に誤り等ある可能性があります
誤りがありましたら教えてくださると幸いです
:::

環境
ruby 3.2.2
Ruby on Rails 7.0.8
Docker 24.0.2



# エラーその１
`docker compose run web bundle exec rspec`を実行すると
```
1.1) Failure/Error: visit root_path
          
          Selenium::WebDriver::Error::WebDriverError:
            unable to connect to /root/.cache/selenium/chromedriver/linux64/122.0.6261.57/chromedriver 127.0.0.1:9515
```
## 解決のためにしたこと
### Gemfileからwebdriverの記述をコメントアウト
```ruby:Gemfile抜粋
group :test do
  gem 'capybara'
  gem 'selenium-webdriver'
  # gem 'webdrivers'
end
```
docker上でテストを行う際は、Seleniumの公式Dockerイメージである`seleniarm/standalone-chromium`を使用します。このイメージにはブラウザとそれに対応するwebdriverがすでに含まれているため`webdrivers`を使用する必要はありません。

### ブラウザが動作するためのコンテナを作成
```ruby:docker-compose.yml:抜粋
  web:
  #省略
    depends_on:
      - db
      - selenium_chrome

  selenium_chrome:
    image: seleniarm/standalone-chromium
    ports:
      - 4444:4444
```
`selenium_chrome`コンテナを追加
私はM1 Macを利用しているため`image`には`seleniarm/standalone-chromium`を使用しています
https://qiita.com/mejiro03/items/76f7080b3ab25cbee8e4
https://qiita.com/se_yataro/items/4a741658495cf43df552

### capybara.rbを作成
`Capybara`の設定をカスタマイズするため`spec/support/capybara.rb`を作成
```ruby:capybara.rb
RSpec.configure do |config|
  config.before(:each, type: :system) 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

  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

# Chrome
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('window-size=1680,1050')

  Capybara::Selenium::Driver.new(app, browser: :remote, url: url, options: options)
```

### rail_helper.rbの修正
```ruby:rails_helper.rb抜粋
# デフォルトでコメントアウトされているので、コメントを外す
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
```
これを外すことで`reqire "rails_helper"`とテストファイル(`hoge_spec.rb`)に記述したとき`spec/support`配下のファイルが自動的に読み込まれます。

# エラーその２
```
     Failure/Error: avatar.variant(resize_to_fit: [400, 400]).processed
     
     LoadError:
       Could not open library 'vips.so.42': vips.so.42: cannot open shared object file: No such file or directory.
       Could not open library 'libvips.so.42': libvips.so.42: cannot open shared object file: No such file or directory.
       Searched in <system library path>, /usr/lib, /usr/local/lib, /opt/local/lib
```
## 解決のためにしたこと

### docker環境にvipsのインストール
gem`image_processing`を使用していましたが、docker環境にもvipsをインストールしなければならない
`Dockerfile`に以下の記述を追加して再度`docker compose up --build`を実行
```dockerfile:Dockerfile
RUN apt-get update -qq && apt-get install -y --no-install-recommends libvips42
```
# エラーその３
`hoge_spec.rb`でcapybaraの`using_session`を使用して2つのセッションを用いてテストコードを書いていたら起きたエラー
```
     2.3) Failure/Error: Unable to infer file and line number from backtrace
          
          Selenium::WebDriver::Error::InvalidSessionIdError:
            invalid session id
```
```
     2.4) Failure/Error: Unable to infer file and line number from backtrace
          
          Selenium::WebDriver::Error::InvalidSessionIdError:
            invalid session id
```
## 解決のためにしたこと
### selenium_chromeコンテナのセッション上限数を拡張
[こちらの記事](https://nekonenene.hatenablog.com/entry/selenium-3-to-4-system-spec-net-read-timeout)に書かれている通り[公式](https://github.com/SeleniumHQ/docker-selenium?tab=readme-ov-file#increasing-session-concurrency-per-container)に`デフォルトでは、SE_NODE_MAX_SESSIONS 環境変数によって、コンテナごとに 1 つのセッションのみが実行されるように設定されている。`と記述されている
そのため`docker-compose.yml`ファイルを以下のように修正した
```docker:docker-compose.yml抜粋
  selenium_chrome:
    image: seleniarm/standalone-chromium
    environment:
      - SE_NODE_MAX_SESSIONS=2 #有効なセッション数を２つに拡張
      - SE_NODE_OVERRIDE_MAX_SESSIONS=true #オーバーライドの設定を有効に
    ports:
      - 4444:4444
```

# エラーその４
エラーその3が無事解決し、`using_session`を使用したテストコードも無事に成功したので、`hoge_spec.rb`に記述している全てのテストをまとめて走らせたら起きたエラー
```
          Selenium::WebDriver::Error::UnknownError:
            unknown error: session deleted because of page crash
            from unknown error: cannot determine loading status
            from tab crashed
              (Session info: chrome-headless-shell=121.0.6167.160)
```
```
     1.2) Failure/Error: Unable to infer file and line number from backtrace
          
          Selenium::WebDriver::Error::InvalidSessionIdError:
            invalid session id
```
## 解決のためにしたこと
### Dockerコンテナに割り当てるリソース量を増やす
```docker:docker-compose.yml抜粋
selenium_chromeコンテナのメモリのリソース量を拡張
  selenium_chrome:
    image: seleniarm/standalone-chromium
    shm_size: 256m #メモリ不足を解消
    environment:
      - SE_NODE_MAX_SESSIONS=2
      - SE_NODE_OVERRIDE_MAX_SESSIONS=true
    ports:
      - 4444:4444```dockerfile:docker-compose.yml
```

# 参考記事
https://peno022.hatenablog.com/entry/docker-rails7-rspec-configuration-for-system-spec
https://nekonenene.hatenablog.com/entry/selenium-3-to-4-system-spec-net-read-timeout
https://zenn.dev/mitsuaki/articles/d2772bb8a8cd6e
https://zenn.dev/felmy/articles/0f534f8f7bee49