Rails + Selenium + DockerでSystemSpecの環境構築

最近はDockerでRails環境を構築する方も多いかと思い、system specができるように環境構築を記事にしてみました。

たった5文字のミスで2週間以上ハマるという泣きたくなる体験もしたので、今後同じ道を辿らないよう遭遇したエラーログたちも残しておきます。

##開発環境
**・Rails 5.1.7**
**・Ruby:2.3.7 (🙇‍♂️)**
**・RSpec 3.8**
**・ChromeDriver 75.0.3770.90**
**・google-chrome-stable 75.0.3770.100**
**・capybara (3.15.1)**
**・DockerImage**

##Gemfile

すでに`rspec-rails`が入ってる方は、`capybara`と`selenium-webdriver`を追加します。

```rb
gem 'rails', '~> 5.1.0'
...
...
group :development, :test do
  gem "rspec-rails"
end

group :test do
  gem 'capybara'
  gem 'selenium-webdriver'
end
```
`selenium-webdriver`を利用することで、ブラウザを自動で操作してテストができるようになります。
注意点としては、最新版を使わないと`chrome`とのバージョンと互換性がとれなくなる可能性があるので、常に最新バージョンに保つようにすべきです。

```
bundle update capybara selenium-webdriver
```

##Dockerfile

DockerImageは[こちら](https://hub.docker.com/_/ruby?tab=tags&page=9)を元にしてます。

ここでは、最新バージョンの`chromedriver`と、`google-chrome-stable`をインストールしていきましょう。

```Dockerfile
FROM ruby:2.3.7

ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential node.js
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
        && apt-get install -y nodejs
# chromeの追加
RUN apt-get update && apt-get install -y unzip && \
    CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
    wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \
    unzip ~/chromedriver_linux64.zip -d ~/ && \
    rm ~/chromedriver_linux64.zip && \
    chown root:root ~/chromedriver && \
    chmod 755 ~/chromedriver && \
    mv ~/chromedriver /usr/bin/chromedriver && \
    sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \
    sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \
    apt-get update && apt-get install -y google-chrome-stable

RUN mkdir /app
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app
CMD ["rails", "server", "-b", "0.0.0.0"]
```

`chromedriver`の配布されてるバージョンは[こちら](https://chromedriver.storage.googleapis.com/index.html)から確認できます。

記事執筆時点での最新バージョンは`75.0.3770.140`でした。

コンテナに入って確認する場合↓

```
chromedriver -v
=> ChromeDriver 75.0.3770.140 (2d9f97485c7b07dc18a74666574f19176731995c-refs/branch-heads/3770@{#1155})
```
```
google-chrome-stable --version
=> Google Chrome 75.0.3770.100 
```


[参考記事](https://qiita.com/hoshitocat/items/c48bfdce867fc852e829)

##Rspecの設定

・Capybaraにドライバを登録
・Seleniumのoptionを指定する
・js使いたいときだけheadlessモードで起動する

みたいなことをします。

```spec/support/selenium_chrome.rb 
require 'capybara/rspec'
require 'selenium-webdriver'

Capybara.register_driver :selenium_chrome_headless do |app|
  options = ::Selenium::WebDriver::Chrome::Options.new


  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-dev-shm-usage')
  options.add_argument('--window-size=1400,1400')


  driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end

Capybara.javascript_driver = :selenium_chrome_headless
```


`sprc/support`以下をincludeさせることを忘れないようにしましょう。

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

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
  end
end
```

注意点としては、**オプションに`--no-sandbox`を指定することです。**これがないと正常に動作しません。

また、使用できるドライバは`:selenium`, `:selenium_headless`, `:selenium_chrome`, `:selenium_chrome_headless`の4つあり非常にややこしいのですが、`:selenium_chrome_headless`を指定しておけば問題ないかと思います。


これで準備が整いました。

##実際にテストを走らせてみる

```spec/system/**.rb
require "rails_helper"

RSpec.describe 'Ideas', type: :system, js: true do
  let(:user) { create(:user, name: "hoge") }

  subject { visit root_path }
  it "ユーザー一覧表示" do
    subject
    expect(page).to have_content("hoge")
  end
end
```

```
1 example, 0 failures
```
ちゃんと通りました。

##苦戦したエラーたち

##Selenium::WebDriver::Error::UnknownError

```
Selenium::WebDriver::Error::UnknownError:
            unknown error: Chrome failed to start: exited abnormally
              (unknown error: DevToolsActivePort file doesn't exist)
              (The process started from chrome location /usr/bin/chromium is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
```
ほとんどの場合、`chrome`の`option`に問題があります。
自分は、`--no-sandbox`をつけ忘れてこのエラーを吐いていました。

必要なオプションについては[こちら](https://github.com/heroku/heroku-buildpack-google-chrome/issues/46#issuecomment-396783086)を参考にしてみると良いかもしれません。


##Failed to read the 'sessionStorage' property from 'Window': Storage is disabled inside 'data:' URLs

`sessionStorage`を読み込めませんよ！というエラー。

```
Selenium::WebDriver::Error::WebDriverError:
   <unknown>: Failed to read the 'sessionStorage' property from 'Window': Storage is disabled inside 'data:' URLs.
   (Session info: headless chrome=75.0.3770.100)
```
しかし、`capybara`でこのエラーを出す猛者はほぼ一人もあわられず、調べても`Anguler`のテストフレームワークである`Protractor`の記事しか出ません。
https://stackoverflow.com/questions/24205856/protractor-empty-local-storage

個人的な原因としては、**pathにアクセスできていなかった**というもの。
SystemSpec側で`visit`をつけ忘れるという。。

`subject { root_path }`　→　`subject { visit root_path }`
としたことで無事解決しました。
OSSまで読んだのに、まさかここだとは思わず1週間以上取られて泣きたい。


動くまでの格闘記を以下に貼っておきます。
https://teratail.com/questions/200250#reply-296669

参考記事:
・https://stackoverflow.com/questions/50610316/capybara-headless-chrome-in-docker-returns-devtoolsactiveport-file-doesnt-exist

・https://github.com/SeleniumHQ/selenium/issues/6088