はじめに
Rspec3.7以降で実装された"System Spec"を動作できるRails+Docker環境の作り方をまとめました。
基本コピペで進められるように書きましたので、初学者の方であっても「まず動く」状態まで持っていけると思います。
この記事では、Docker、docker-composeがすでにインストールされた状態からスタートします。私は、自PC(Mac)のホストOSに「Docker for Mac」をインストールしています。
著者動作環境における、Docker、docker-composeのバージョンは以下の通りです。
$ docker --version
Docker version 19.03.5, build 633a0ea
$ docker-compose -v
docker-compose version 1.25.4, build 8d51620a
DockerでのRails環境の構築
まずは、Docker公式が用意してくれているRals用のクイックスタート「Quickstart: Compose and Rails」をベースに、Railsが動作するDocker環境を作成していきます。
一部、公式のファイル内容に変更を加えていますので、ご注意ください。
早速始めていきましょう。作業ディレクトリ用意し、cdで移動します。
$ mkdir sample_app
$ cd sample_app
Quickstartに則り、sample_app内に下記5ファイルを作成します。
$ touch Dockerfile docker-compose.yml Gemfile Gemfile.lock entrypoint.sh
/sample_app
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── entrypoint.sh
└── docker-compose.yml
各ファイルを下記の通り編集します。5ファイルのうち、docker-compose.yml以外は、Quickstart公式のコピペです。vim等、お好きなテキストエディタをお使いください。
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp
# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
source 'https://rubygems.org'
gem 'rails', '~>5'
(空のまま)
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
version: '3'
services:
db:
image: postgres
volumes:
- ./tmp/db:/var/lib/postgresql/data
environment:
- "POSTGRES_USER=xxxx" # 追記
- "POSTGRES_PASSWORD=xxxx" # 追記
web:
build: .
command: bash -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:
- "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub" # 追記
selenium_chrome:
image: selenium/standalone-chrome-debug # 追記
上記5ファイルのうち、docker-compose.ymlのみ、編集で手を加えています。具体的には、
・postgresにユーザー名、パスワードを指定
・System Specを動作させるためのコンテナイメージ「selenium/standalone-chrome-debug」を追加
ファイルを作成したら、Railsアプリを新規作成します。
$ docker-compose run web rails new . --force --no-deps --database=postgresql
$ ls
Dockerfile README.md bin db lib public tmp
Gemfile Rakefile config docker-compose.yml log storage vendor
Gemfile.lock app config.ru entrypoint.sh package.json test
rails newに伴い、Gemfileにデフォルトのgemがインストールされました。
Gemfileに編集を加えます。具体的には、
・"rspec-rails"を追加
・"chromedriver-helper"を削除
# 省略
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'rspec-rails' # 追記
end
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Easy installation and use of chromedriver to run system tests with Chrome
# gem 'chromedriver-helper' # 削除
end
#省略
Gemfileを更新したため、再ビルドします。
$ docker-compose build
これで、新しいGemfileでbundle installが実行されました。
続いて、configディレクトリ内のdatabase.ymlを編集します。
default: &default
adapter: postgresql
encoding: unicode
host: db
username: xxxx
password: xxxx
pool: 5
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
DBを作成します。
$ docker-compose run web rails db:create
Starting sample_app_db_1 ... done
Created database 'myapp_development'
Created database 'myapp_test'
コンテナを起動します。このタイミングで、selenium/standalone-chrome-debugのプルが実行されます。
$ docker-compose up -d
Pulling selenium_chrome (selenium/standalone-chrome-debug:)...
latest: Pulling from selenium/standalone-chrome-debug
.
.
.
イメージプルが終了しましたら、docker-composeが正常に立ち上がっているか、確認してみましょう。
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------
sample_app_db_1 docker-entrypoint.sh postgres Up 5432/tcp
sample_app_selenium_chrome_1 /opt/bin/entry_point.sh Up 4444/tcp, 5900/tcp
sample_app_web_1 entrypoint.sh bash -c rm - ... Up 0.0.0.0:3000->3000/tcp
web,db,selenium_chromeの3つが立ち上がっていれば、OKです。ついでに、http://localhost:3000 にアクセスし、いつもの「Yay! You’re on Rails!」が表示されているか、確認しましょう。
また、RSpecが正確にインストールされているかも確認してみましょう。
$ docker-compose run web rspec -v
Starting sampleApp_db_1 ... done
RSpec 3.9
- rspec-core 3.9.1
- rspec-expectations 3.9.0
- rspec-mocks 3.9.1
- rspec-rails 3.9.0
- rspec-support 3.9.2
各バージョンは状況により異なるかと思いますが、RSpec 3.7以上のバージョンであれば、System Specを動作させることができます。
RSpecの初期設定
無事、必要なgemが揃った状態のDockerコンテナを用意できたので、ここからは中身の諸々の設定をいじっていきます。
まず、Railsにデフォルトで配置されているtestディレクトリは使用しないため、削除してしまいましょう。
$ rm -rf test
RSpecをインストールします。
$ docker-compose run web rails generate rspec:install
これにより、以下のディレクトリ構造に対して、計3ファイルが生成・配置されます。
/sample_app
├── .rspec
└── spec
├── rails_helper.rb
└── spec_helper.rb
.rpsecファイルを編集します。2行目のコードを追記してください。これにより、テスト結果の表示形式が変更されます。
--require spec_helper
--format documentation
デフォルトでは、”失敗したテスト”のみがターミナル上に出力されるのですが、この変更を行うことにより、”成功・失敗両方のテスト”の結果を出力してくれるようになります。必須ではありませんが、設定しておくことをオススメします。
続いて、spec/rails_helper.rbについて、25行目あたりに書かれている下記コードのコメントアウトを外してください。
# 省略
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
# 省略
これにより、後ほど定義するCapybara用の設定ファイルを読み込めるようになります。
specファイルの自動生成機能OFF
デフォルトでは、controllerやviewを作成したタイミングで、対応するspecファイル(=テストコードを書くためのファイル)が自動生成されます。
今回は初学者にとっての混乱を防ぐため、自動生成はOFFにしておきます。config/application.rbに下記コードを追加します。
equire_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Myapp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
### 追記 ######################################
config.generators do |g|
g.test_framework :rspec,
view_specs: false,
helper_specs: false,
controller_specs: false,
routing_specs: false,
request_specs: false
end
##############################################
end
end
Capybaraの初期設定
Capybaraに設定を加えるため、capybara.rbファイルを作ります。
$ mkdir spec/support
$ touch spec/support/capybara.rb
require 'capybara/rspec'
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :selenium, using: :headless_chrome, options: {
browser: :remote,
url: ENV.fetch("SELENIUM_DRIVER_URL"),
desired_capabilities: :chrome
}
Capybara.server_host = 'web'
Capybara.app_host='http://web'
end
end
これで、SystemSpecが動作する環境が整いました。
System Spec実践
それでは実際に、静的ページを用いてSystemSpecを使用してみましょう。
今回は、
・rootページにアクセスし、"Hello World!"が表示されていることを検証
・rootページに、"Helpページ"へのリンクが置かれていることを検証
・リンク先のHelpページに、"This is the help page."が表示されていることを検証
という流れのテストを書いてみたいと思います。
下準備として、home,helpを持ったStaticPagesコントローラーを作成します。
$ docker-compose run web rails generate controller StaticPages home help
ルートとビューをいじります。
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
end
<h1>StaticPages#home</h1>
<p><%= link_to "Help", help_path %></p>
<h1>This is the help page.</h1>
下準備が終わったため、specファイルを書いていきます。specディレクトリ下にsystemディレクトリを作成し、その中に"homes_spec.rb"を配置します。
$ mkdir spec/system
$ touch spec/system/homes_spec.rb
spec/system/homes_spec.rbを編集します。
require 'rails_helper'
RSpec.describe 'Home', type: :system do
it 'shows greeting' do
# root_pathへアクセス
visit root_path
# ページ内に'Hello World!'が含まれているかを検証
expect(page).to have_content 'Hello World!'
# 'Help'文字列をクリック
click_on 'Help'
# ページ内に'This is the help page.'が含まれているかを検証
expect(page).to have_content 'This is the help page.'
end
end
いよいよ動かします!今回用意した環境でSystem Specを動かすためには、webコンテナ内に入る必要があります。
$ docker-compose exec web bash
rspecは、"rails spec"コマンドで実行できます。
$ rails spec
/usr/local/bin/ruby -I/usr/local/bundle/gems/rspec-core-3.9.1/lib:/usr/local/bundle/gems/rspec-support-3.9.2/lib /usr/local/bundle/gems/rspec-core-3.9.1/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb
Home
Capybara starting Puma...
* Version 3.12.4 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://web:45873
shows greeting
Finished in 2.33 seconds (files took 8.35 seconds to load)
1 example, 0 failures
無事、テストが通ればOKです!!
スクリーンショット保存機能
ちなみにですが、System Specには、「失敗時の画面をスクリーンショットで自動保存する」という便利機能があります。
ビューをいじって、わざとテストを失敗させてみましょう。
<h1>StaticPages#home</h1>
<p><%= link_to "Hel", help_path %></p> # "Help"をタイポしてしまったケース
$ rails spec
.
.
.
Home
Capybara starting Puma...
* Version 3.12.4 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://web:44173
shows greeting (FAILED - 1)
Failures:
1) Home shows greeting
Failure/Error: click_on 'Help'
Capybara::ElementNotFound:
Unable to find link or button "Help"
[Screenshot]: tmp/screenshots/failures_r_spec_example_groups_home_shows_greeting_612.png
.
.
.
「'Help'が見つかりません!」とメッセージが出され、tmp/screenshotsにエラーが発生したタイミングでのブラウザ画面が保存されています。
"Help"と書いたつもりのところが"Hel"になってしまっているのが、確認できます。
最後に
以上です!不明な点、間違っている点などがありましたら、私のTwitter(@ddpmntcpbr)のDMまで連絡頂けると幸いです。