Docker環境でRspec×Capybara×ChromeDriverを動作させる

作成したrailsアプリをDocker環境にのせ、開発環境上で問題なく動作するよう設定しました。
しかし、この状態のままではRspecが正しく動作しなくなっていたため、内容を見直しました。

RspecやCapybaraを元々利用していた前提であり、それらの導入については記載しておりません。
また、前回書いたDocker導入記事の続きの記事となります。Dockerについての設定内容はこちらをご参照ください。
[ローカルRails6アプリのDocker環境への移行（ついでにMysql導入） - Qiita](https://qiita.com/ryohei_kh/items/5583e782a81edae21733)

##環境

* ruby 2.7.2
* Rails 6.0.3
* Mysql 8.0.22
* rspec-rails 4.0.2
* capybara 3.34.0
* selenium-webdriver 3.142.7

##`docker-compose.yml`
元々は`db`と`web`と`webpacker`のみの構成としていましたが、新たにテスト用のDBの`test-db`と、CapybaraでのChrome実行用の`chrome`を追加しました。
`web`にChromeをインストールする手もありますが、コンテナに複数の機能をもたせるのはDockerの設計思想に合っていないと思い、この構成としました。

```yaml:docker-compose.yml
version: '3'

services:
  db:
    image: mysql:8.0.22
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    ports:
      - '3306:3306'
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: ${DEV_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DEV_ROOTPASS}
      MYSQL_USER: ${DEV_USERNAME}
      MYSQL_PASSWORD: ${DEV_USERPASS}

  ### 追加ここから ###
  test-db:    #Rspecでのテスト実行用のコンテナ
    image: mysql:8.0.22
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    ports:
      - '4306:3306'    #開発用とはポート番号を別にする
    environment:    #接続情報は開発のものと一応分けます
      MYSQL_DATABASE: ${TEST_DATABASE}
      MYSQL_ROOT_PASSWORD: ${TEST_ROOTPASS}
      MYSQL_USER: ${TEST_USERNAME}
      MYSQL_PASSWORD: ${TEST_USERPASS}
  ### 追加ここまで ###

  web: &app_base
    build:
      context: .
      dockerfile: ./docker/rails/Dockerfile
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b 0.0.0.0"
    depends_on:
      - db
      - chrome  #追加
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - gem_modules:/vendor/bundle
      - node_modules:/node_modules
    tty: true
    stdin_open: true
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
      BUNDLE_APP_CONFIG: ./.bundle
      SELENIUM_DRIVER_URL: http://chrome:4444/wd/hub    #追加、どのコンテナのブラウザを使用するか指定

  webpacker:
    <<: *app_base
    command: bash -c "bundle exec bin/webpack-dev-server"
    depends_on:
      - web
    ports:
      - '3035:3035'
    tty: false
    stdin_open: false
    environment:
      BUNDLE_APP_CONFIG: ./.bundle
      NODE_ENV: development
      RAILS_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0

  ### 追加ここから ###
  chrome:    #Chromeでのテスト実行用コンテナ
    image: selenium/standalone-chrome    #Chromeがインストールされたイメージ
    ports:
      - '4444:4444'
  ### 追加ここまで ###

volumes:
  db_data:
  gem_modules:
  node_modules:
```

##`database.yml`
`test-db`に合わせ設定しました。

```diff_yaml
:config/database.yml
 default: &default
   adapter: mysql2
   username: app
   password: password
   host: db    #サービス名を指定

 development:
   <<: *default
   database: mysql_development

+test
+  <<: *default
+  database: mysql_test
+  host: test-db
```

##`.env`
現状の設定内容では、分ける必要がないかもしれませんが、念のため分けて定義しています。

```diff:.env
 DEV_DATABASE=mysql_development
 DEV_USERNAME=app
 DEV_USERPASS=password
 DEV_ROOTPASS=password
+TEST_DATABASE=mysql_test
+TEST_USERNAME=app
+TEST_USERPASS=password
+TEST_ROOTPASS=password
```

##`capybara.rb`
`.rsepc`に`--require rails_helper`と記載することで、`spec/rails_helper`を読み込んでいます。
その中の以下の行を有効にすることで、`spec/support/capybara.rb`を読み込んでいます。

```ruby:spec/rails_helper.rb
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
```

`capybara.rb`は以下のような記載にしています。

```ruby:spec/support/capybara.rb
Capybara.register_driver :remote_chrome do |app|
  url = ENV['SELENIUM_DRIVER_URL']
  caps = ::Selenium::WebDriver::Remote::Capabilities.chrome(
    'goog:chromeOptions' => {
      'args' => [
        'no-sandbox',
        'headless',
        'disable-gpu',
        'window-size=1680,1050'
      ]
    }
  )
  Capybara::Selenium::Driver.new(app, browser: :remote, url: url, desired_capabilities: caps)
end

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 :selenium_chrome_headless
    Capybara.server_host = IPSocket.getaddress(Socket.gethostname)
    Capybara.app_host = "http://#{Capybara.server_host}"
    driven_by :remote_chrome
  end
end
```

元々javascriptをテストできるよう、`driven_by :selenium_chrome_headless`としておりました。
今回は`:remote_chrome`というドライバーを設定し、`browser: :remote`とすることで、`web`コンテナのブラウザを使用するのではなく`chrome`コンテナのブラウザを使用するように設定しています。
（そもそも`web`コンテナにはブラウザが入っていないので、このままだとエラーとなってしまいます。）

##`Gemfile`
元々ローカル環境でrspecを導入していたため、`rspec-rails`や`capybara`等、必要なgemは導入済みです。
今回のDocker環境でのRspecにあたり以下の記述を修正しました。

```diff_ruby:Gemfile

  group :test do
-   gem 'webdrivers'
+   gem 'webdrivers', require: !ENV['SELENIUM_DRIVER_URL']
  end
```
この`require`の記載がないと、`capybara.rb`の`driven_by :remote_chrome`で以下のようなエラーが発生してしまうので付与しています。


```
Webdrivers::BrowserNotFound: Failed to find Chrome binary.
```

おそらくホストのブラウザを探しにいくことによるエラーだと思われます。

参考：[Failed to find Chrome binary with Rails 6 rc2 · Issue #148 · titusfortner/webdrivers](https://github.com/titusfortner/webdrivers/issues/148)


##おわりに
以下のコマンドを実行することにより、問題なく動作することを確認できました。

```
docker-compose up -d
docker-compose exec web bin/rspec
```

`chrome`コンテナのブラウザを使用するところで苦戦しましたが、無事に動作するようになりよかったです。
次はこの環境をCircleCIで自動テストできるようにしたいと思います。

##参考記事
**・[willnet/capybara-readme-ja: Capybara の README 和訳](https://github.com/willnet/capybara-readme-ja)**
**・[Rails on Docker(alpine)でdocker-seleniumを使わないでSelenium+RSpec+Capybaraでテスト自動化してみる - Qiita](https://qiita.com/at-946/items/6def89bb94f748ad97f2)**
**・[Rails + Selenium + DockerでSystemSpecの環境構築 - Qiita](https://qiita.com/ngron/items/f61b8635b4d67f666d75)**
**・[初めてDocker + RailsでE2EテストしようとしてWebdrivers::BrowserNotFound: Failed to find Chrome binary. エラーにはまった時の話 - Qiita](https://qiita.com/m_suzu/items/08970f14488690dc6f8e)**