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


Abstract

以前、Rails on Dockerでdocker-seleniumを利用してSelenium+RSpec+Capybaraのテストを実行する方法をメモしてました。
--> Rails on Dockerにて、Headless ChromeでSystem Testをやってみた。 - Qiita

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

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

コンテナのベースは以下のRails on Docker(alpine linux)で。
--> Rails on DockerのQuickstartをalpine linuxでやってみる - Qiita

環境準備

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

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ブラウザを立ち上げるためのパッケージたちです。

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を追加します。

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に以下を追加します。

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をデフォルトで読み込むように設定しておきます。

.rspec
--require rails_helper

System Testを書いてみる

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

$ mkdir spec/system

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

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の影響で異なってしまったのが原因かと思います。webdriversrails newのときに-Tオプションをつけていない場合デフォルトで導入されるので要チェックです。

Testを失敗させてスクリーンショットをみてみる

試しにテストを失敗させてみます。

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


お、失敗しましたね。
テストが失敗するのはいいんですが、なにやらscreenshotが真っ白...

Rails6.0.0 + RSpec3.8だとこういうことが起きちゃうらしいですね。
Rails アプリケーションの不安定なテストを撲滅したい 〜system spec のデバッグ方法とテストを不安定にさせる要因〜 - あらびき日記 を参考に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
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がちゃんと表示されるはずです。

Conclusion

Rails on DockerのQuickstartをalpine linuxでやってみる - Qiita の記事からテスト自動化の追加設定ができました。screenshotはどっかのバージョンで対応されるのかなー。

Reference