Railsチュートリアルの2周目を、Docker環境でかつテストをRspecに代えて進めていたのですが、System specを導入しようとしたところで挫折…。
いろいろ調べたり、指導者の方に聞いたりして解決しましたので、同じように迷っている人の為になればと思い、書き残してみます。
そもそもSystem specとはなんぞや?
System specについて知らない人はこちらの記事が参考になると思います。
rspec-rails 3.7の新機能!System Specを使ってみた
簡単に説明すると、エンドツーエンド(E2E)テストを実行するための機能です。みなさんが実際にブラウザを利用するようにボタンやリンクを押してどの画面に遷移しただとか、その画面にはある特定のHTMLタグが含まれているかどうかなどをテストすることができます。
使ってみればわかると思いますが、とてもわかりやすく便利です。
ちなみに、System specはrspec-rails 3.7以降から導入されたのですが、その前はFeature specというのが使われていました。実はほとんど同じなので、Feature specで進めても良いのですが、公式で推奨されているのでSystem specを使ってみたいと思います。
System(Feature) specで使えるいろいろな機能は以下の記事を参考にしてください。
使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」
さらにそもそもRSpecやDockerに詳しくない方へ
RSpec→使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
Docker→【図解】Dockerの全体像を理解する -前編-
※ Dockerがインストールされていることを前提に話を進めますので、忘れずにインストールしておいてくだい。
では、本題に入りましょう。
ちなみに、一から作成していくので、すでにRailsでDocker環境を持っている人は適宜置き換えて試してみてください。
docker-composeを用いてRails Projectを作成する
完成版をおいておきます。
https://github.com/tegnike/system_spec
ではまず、以下のファイルを作成しましょう。
- Dockerfile
- docker-compose.yml
- Gemfile
- Gemfile.lock
RailsのDocker環境作成に関しては以下の記事を参考にしています。
docker-compose で Rails の開発環境を作る
FROM ruby:2.4.2
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev postgresql-client unzip
RUN 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 /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install
ADD . /app
WORKDIR /app
System specではGoogle Chromをインストールしておく必要があるので、3行目のRUNでそれを実行しています。
db:
image: postgres
expose:
- "5432"
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/app
ports:
- "3000:3000"
links:
- db
source 'https://rubygems.org'
gem 'rails', '~> 5.1.4'
Gemfile.lockは中身は空のまま、ファイルだけ作成ください。
ちなみに、Dockerfileの3行目のRUNでsystem specに必要なGoogle Chromeをwebコンテナにインストールしています。
これらのファイルが用意できたら、それらがあるフォルダで以下のコマンドを実行してください。
$ docker-compose run web rails new . --force --database=postgresql --skip-test
このコマンドを実行することで、docker-compose.ymlに定義したwebコンテナを一時起動し、Rails Projectを作成することができます。
フォルダ下にRailsのファイルたちが追加されていますね。
では、更新されたGemfileにrspecでのテストに必要な下記のgemを追記しましょう。
gem 'therubyracer', platforms: :ruby # 先頭の"#"を消す
### 省略 ###
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
gem 'capybara' # 追記
gem 'selenium-webdriver' # 追記
gem 'webdrivers' # 追記
gem 'launchy', '~> 2.4.3' # 追記
end
では、更新したGemfileをimageに含めるためにbuildを実行しましょう。
$ docker-compose build
次に、database.ymlを編集します。
今回はデータベースにpostgresqlを使用するので、以下のように設定してください。
default: &default
adapter: postgresql
encoding: unicode
username: postgres # 追記
host: db # 追記
password: # 追記
以下のコマンドでデータベースを作成します。
$ docker-compose run web rake db:create
これで一通り準備が整いましたので、下記のコマンドでコンテナを立ち上げてから localhost:3000
にアクセスすれば、お馴染みの**Yay! You're on Rails!**を見ることができます。
$ docker-compose up -d
System specを導入する
では、System specを導入していきましょう。
まず、下記のコマンドを実行することでRSpecの設定ファイルである spec_helper.rb
と rails_helper.rb
の2つのファイルが作成されます。
$ docker-compose run web rails generate rspec:install
そして rails_helper.rb
は以下のように修正してください。
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] = 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'capybara/rspec'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
config.before(:each, type: :system) do
driven_by :selenium_chrome
end
config.before(:each, type: :system, js: true) do
driven_by :selenium_chrome
end
end
もう1つ、 capybara.rb
というファイルを作ります。これはまだ作成されていないので、specフォルダの下にsupportファイルを作り、その下に配置しましょう。
Capybara.default_driver = :selenium_chrome
Capybara.javascript_driver = :selenium_chrome
Capybara.register_driver :selenium_chrome do |app|
options = ::Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1400,1400')
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
これでSystem specの設定は完了です。
System specでテストしてみよう
せっかく作ったので試してみたいのですが、今のままだとモデルがなくてテストができないので、 scaffold
コマンドでUserモデルを作成します。ついでにマイグレーションも実行しましょう。
※ ちなみに今回はSystem specが動くかどうか確認することを目的としていますので、複雑なテストは書きません。
$ docker-compose run web bin/rails g scaffold User name:string
$ docker-compose run web rake db:migrate
specフォルダ下にSystem spec用のフォルダを作成し、そこに users_spec.rb
を作成します。
require 'rails_helper'
RSpec.describe 'Users', type: :system, js: true do
before do
@user = User.create!(name: 'Test User')
end
it 'ユーザ名が表示されること' do
visit user_path(@user)
expect(page).to have_content @user.name
end
end
では、テストを実行してみましょう。
$ docker-compose up
$ docker-compose run web rspec spec/system/users_spec.rb
Starting system_spec_db_1 ... done
.
Finished in 5.18 seconds (files took 4.95 seconds to load)
1 example, 0 failures
はい、問題なく成功しました!
まとめ
この記事では、System Specを使えるDocker環境の構築を説明しました。
冒頭でも説明しましたが、Feature Specとほとんど一緒のSystem Specですが、確実に上位互換なので導入しておいて損はないでしょう(公式でも推奨されていますしね)。
では、また。