Rails on Docker(alpine)でdocker-seleniumを使わないでSelenium+RSpec+Capybaraでテスト自動化してみる

# Abstract
以前、Rails on Dockerで[docker-selenium](https://github.com/SeleniumHQ/docker-selenium)を利用してSelenium+RSpec+Capybaraのテストを実行する方法をメモしてました。
--> [Rails on Dockerにて、Headless ChromeでSystem Testをやってみた。 - Qiita](https://qiita.com/at-946/items/e96eaf3f91a39d180eb3)

System testは記事でもメモった通り色々と便利。

ただ、Railsアプリケーションが動くコンテナ内でテストを完結することができたのでそちらのメモを残します。

コンテナのベースは以下のRails on Docker(alpine linux)で。
--> [Rails on DockerのQuickstartをalpine linuxでやってみる - Qiita](https://qiita.com/at-946/items/c69a512ea47941747b18)

# 環境準備
環境準備として、`Dockerfile`と`docker-compose.yml`をいじっていきます。

```dockerfile:Dockerfile
FROM ruby:2.6.5-alpine3.10

ENV RUNTIME_PACKAGES="linux-headers libxml2-dev make gcc libc-dev nodejs tzdata postgresql-dev postgresql yarn vim" \
    CHROME_PACKAGES="chromium-chromedriver zlib-dev chromium xvfb wait4ports xorg-server dbus ttf-freefont mesa-dri-swrast udev" \
    BUILD_PACKAGES="build-base curl-dev" \
    ROOT="/myapp" \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

WORKDIR ${ROOT}
COPY Gemfile ${ROOT}
COPY Gemfile.lock ${ROOT}

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --no-cache ${CHROME_PACKAGES} && \
    apk add --virtual build-packages --no-cache ${BUILD_PACKAGES} && \
    bundle install && \
    apk del build-packages

COPY . ${ROOT}

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```
Dockerfileについては、`CHROME_PACKAGES`のインストールを追加しています。`CHROME_PACKAGES`がこのコンテナ内でChromeブラウザを立ち上げるためのパッケージたちです。

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

services:
  db:
    image: postgres:12.0-alpine
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      - TZ=Asia/Tokyo

  web:
    build: .
    command: ash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      - RAILS_SYSTEM_TESTING_SCREENSHOT=inline
```
`web`コンテナがRailsアプリケーションが実行されるコンテナですが、環境変数として`RAILS_SYSTEM_TESTING_SCREENSHOT=inline`を追加しています。macのiTerms2を利用している場合は、これを設定しておくとRSpecのSystem TestでNGがあったときにスクリーンショットがiTerm2で表示されるようになるので便利です。

# RSpecのインストール
Gemfileに[rspec-rails](https://github.com/rspec/rspec-rails)を追加します。

```ruby:Gemfile
group :test do
  gem 'rspec-rails', '~>3.8'
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
end
```

一度リビルドしてRSpecをインストールしていきます。

```
$ docker-compose build
$ docker-compose run --rm web rails g rspec:install
```

# RSpecの環境設定
Headless Chromeをデフォルトのドライバーに設定します。`rails_helper.rb`に以下を追加します。

```rb:spec/rails_helper.rb
Capybara.register_driver :selenium_chrome_headless do |app|
  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('--window-size=1680,1050')
  Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end

RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :selenium_chrome_headless
  end
end
```

`rails_helper`をデフォルトで読み込むように設定しておきます。

```text:.rspec
--require rails_helper
```

# System Testを書いてみる
まずはtestコードの格納する場所を作っていきます。

```
$ mkdir spec/system
```

ではテストコードを書いていきます。`type`を`system`に指定することでRSpecのSystemテストとして実行させれます。

```rb:spec/system/sample_spec.rb
feature "sample", type: :system do
  scenario "sample" do
    visit root_path
    expect(current_path).to eq root_path
  end
end
```

ファイル名に`_spec`とつけているのはポイントです。デフォルトで、`_spec`と名付けられているファイルをテストファイルとしてRSpecが判断してテストを実行してくれます。
このテストコードだと、ルートパスにアクセスして、ルートパスにたどり着けたことを検証してます。

```
$ docker-compose run --rm web rspec
Capybara starting Puma...
* Version 3.12.1 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:44815
.

Finished in 15.43 seconds (files took 12.21 seconds to load)
1 example, 0 failures
```
成功しましたか？

ちょっとトラブルシューティング。

```
ChildProcess::LaunchError:
No such file or directory - /root/.webdrivers/chromedriver
```
RSpec実行時にこんなエラーが出ました。Gemfileの`webdrivers`を削除すると改善されました。おそらくchromedriverのpathが`webdrivers`の影響で異なってしまったのが原因かと思います。`webdrivers`は`rails new`のときに`-T`オプションをつけていない場合デフォルトで導入されるので要チェックです。

# Testを失敗させてスクリーンショットをみてみる
試しにテストを失敗させてみます。

```rb:spec/system/sample_spec.rb
feature "sample", type: :system do
  scenario "sample" do
    visit root_path
    expect(current_path).to eq new_user_path
  end
end
```

```
$ docker-compose run --rm web rspec
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/610caf45-d158-b77d-dc80-643b81d7793a.png)
お、失敗しましたね。
テストが失敗するのはいいんですが、なにやらscreenshotが真っ白...

Rails6.0.0 + RSpec3.8だとこういうことが起きちゃうらしいですね。
[Rails アプリケーションの不安定なテストを撲滅したい 〜system spec のデバッグ方法とテストを不安定にさせる要因〜 - あらびき日記](https://abicky.net/2019/09/17/062506/) を参考にhelperを作っていきます。

まず、`rails_helper.rb`でテストサポート用のファイルの場所を定義している箇所があるのでコメントアウトをはずします。

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

これで、`spec/support/**/*.rb`のファイルがテスト実行時に呼び出されるようになりました。ので、そこにhelperファイルを作っていきます。

```
$ mkdir -p spec/support/helpers
$ touch spec/support/helpers/visible_screenshot_helper.rb
```

```rb:spec/support/helpers/visible_screenshot_helper.rb
module VisibleScreenshotHelper extend ActiveSupport::Concern

  included do |example_group|
    example_group.after do
      take_failed_screenshot
    end
  end

  def take_failed_screenshot
    return if @is_failed_screenshot_taken
    super
    @is_failed_screenshot_taken = true
  end

end

RSpec.configure do |config|
  config.include VisibleScreenshotHelper, type: :system
end
```

完成です。もう一度RSpecを実行してみましょう。次はscreenshotがちゃんと表示されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/686edb98-09bb-b6e1-e33f-02557a1bbeb6.png)

# Conclusion
[Rails on DockerのQuickstartをalpine linuxでやってみる - Qiita](https://qiita.com/at-946/items/c69a512ea47941747b18) の記事からテスト自動化の追加設定ができました。screenshotはどっかのバージョンで対応されるのかなー。

# Reference
- [Rails on DockerのQuickstartをalpine linuxでやってみる - Qiita](https://qiita.com/at-946/items/c69a512ea47941747b18)
- [Rails on Dockerにて、Headless  ChromeでSystem Testをやってみた。 - Qiita](https://qiita.com/at-946/items/e96eaf3f91a39d180eb3)
- [Rails アプリケーションの不安定なテストを撲滅したい 〜system spec のデバッグ方法とテストを不安定にさせる要因〜 - あらびき日記](https://abicky.net/2019/09/17/062506/)
- [Capybara in Alpine Linux (Ruby on Rails testing)](https://daniel-starling.com/blog/2018/03/10/capybara-in-alpine-linux/)