はじめに
RSpecをRailsアプリで使用する際、「どのgem入れなきゃいけないんだっけ」「あの設定ってどうやって書くんだっけ?」といったことが多かったので、自分なりの初期設定をまとめてみました。
support配下のファイル有効化
RSpecのテストで共通化したい処理は、モジュールにしてspec/support配下に置くのがお作法。
今回は、
- spec内で共有したい処理は
spec/support/helper
ディレクトリ配下にモジュールを作成 - initialize時に読み込む設定ファイルは
spec/support/config
ディレクトリ配下に作成
という方針をとり、それぞれspec/rails_helper.rb
から読み込むよう設定。
Dir[Rails.root.join("spec/support/config/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/helper/*.rb")].each { |f| require f }
指定したテストタイプで、helper配下に作成したモジュールを読み込むことで、spec内でモジュールに定義された処理が使用できる。
RSpec.configure do |config|
config.include ◯◯(モジュール名), type: :☓☓(使用するテストのタイプ)
end
各spec内で読み込むmoduleを都度指定しても良い。
RSpec.feature 'Home', type: :system do
include ◯◯(モジュール名)
end
gemの追加
諸々必要となるgemを追加し、設定を施していきます。
rspec-rails
RSpecの機能をRailsで使用するためのgem
gem 'rspec-rails'
gem追加後、下記コマンドを実行
$ rails g rspec:install
コマンド実行後、下記ファイルが作成される
create .rspec
create spec/spec_helper.rb #RSpecの全体的な設定を書く
create spec/rails_helper.rb #Rails特有の設定を書く
factory_bot
テストデータ生成用のgem
gem 'factory_bot_rails'
下記設定により、FactoryBotのデータ呼び出しを簡略化できる。
(テストデータの呼び出しを、FactoryBot.create(:◯◯)
→ create(:◯◯)
に簡略化出来る)
require 'factory_bot'
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
また、springを使用してrspecを動かしていると、factoryで作成したデータが正しく読み込まれないことがある。
そんな時は、下記を設定ファイルに追記して、毎回全てのexample実行前にfactory_botを再読込させる。
require 'factory_bot'
RSpec.configure do |config|
config.before :all do
FactoryBot.reload
end
end
spring-commands-rspec
springでrspecを使用することで高速化を図る。
gem 'spring-commands-rspec'
gem追加後、下記コマンド実行で、bin/rspecが作られる
$ spring binstub rspec
bin/rspec作成後は、下記コマンド実行でrspecをspringで使用できる。
$ bin/rspec
capybara
インテグレーション・テストの補助ツール
gem 'capybara'
capybaraの読み込み
require 'capybara/rspec'
launchy
feaatureテスト中にsave_and_open_page
を記載することで、キャプチャのHTMLを自動で開くことが出来るgem
gem 'launchy'
chromedriver-helper
seleniumとchromeのインターフェース
gem 'chromedriver-helper'
jsを使用するsystem specの場合はchromeのヘッドレスドライバを使用するよう設定する。
(※jsを使用しないテストの場合は、高速なRack::Testドライバを使用)
require 'capybara/rspec'
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
shoulda-matchers
リッチなマッチャが使えるようになる。
shoulda-matchers
gem 'shoulda-matchers'
# rails5以降の場合、
gem 'shoulda-matchers', git: 'https://github.com/thoughtbot/shoulda-matchers.git', branch: 'rails-5'
requireした上で、初期設定を施す。
require 'shoulda/matchers'
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
VCR
API呼び出し時に、一回目の呼び出しを記録して、次回以降は記録されたレスポンスを返してくれる
(ちなみに、VCRは、Video Cassette Recordeの略)
gem 'vcr'
gem 'webmock' #vcrの処理時に水面下で利用されるHTTPスタブ化ライブラリ
外部HTTPリクエスト送信時にVCRを使用する設定を追加
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = "#{ ::Rails.root }/spec/cassettes"
config.hook_into :webmock
config.ignore_localhost = true
config.configure_rspec_metadata!
end
exampleのオプションにvcr: true
をつけることで、外部HTTPリクエストがVCR経由で行われるようになる。
it 'hoge', vcr: true do
exampleの中身
end
simplecov
rspec実行時にcoverageというフォルダが自動で作成され、その中にテストのカバレッジを計測したHTMLファイルを自動で配置してくれるようになる。
gem 'simplecov'
require 'simplecov'
SimpleCov.start 'rails'
あとはいつも通りテストを実行するだけ。
database_cleaner
テスト毎にDBをリセットすることで、テストの独立性を担保できる。
gem 'database_cleaner'
DatabaseCleanerの設定を追加
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
これで、specを実行するたびにDB内のデータが削除される。
※Rails5.1以上では、デフォルトでDatabaseCleanerが組み込まれているため、gemをインストールする必要はありません。
初期設定に含めていないgem達
名前はよく聞くけども、各々の理由で初期設定に含めていないgemも列挙しておきます。
必要に応じて追加してください。
faker
ダミーデータの生成を楽にしてくれるgem。
特に必要性を感じてないので入れてないです。
funbar
specの進捗状況を可視化してくれるgem(全体でいくつexampleがあって、そのうち何個終了したか等が見やすくなる)
むしろ見づらくなってる気がするので入れてないです。(主観ですw)
.rspec
RSpecの設定ファイル
下記のように設定。
--color
--require spec_helper
--format documentation
タグ
各exampleに、独自のタグを設定することが出来る。
focusタグがついたspecがあればそれだけ実行、なければ全spec実行する設定を追加。
RSpec.configure do |config|
config.filter_run focus: true
config.run_all_when_everything_filtered = true
end
exampleにfocusタグを付ける場合は、こんな感じ
it 'hoge', :focus do
exampleの中身
end
seedデータの挿入
テスト開始前にDBにデータを入れておきたい場合、ありますよね。
僕はいつもseed-fuを使用しているので、
User.seed(:id,
{ id: 1,
name: 'User',
email: 'user@user.jp',
password: 'password',
password_confirmation: 'password',
},
)
こんな感じでspec実行前に挿入したいseedデータを、db/fixtures/test配下に用意しておいて、
RSpec.configure do |config|
config.before :suite do
fixture_paths = "#{Rails.root}/db/fixtures/test"
SeedFu.seed(fixture_paths)
end
end
こんな感じで、テスト実行前にデータを挿入してます。
ちなみにこのSeedFu.seed
の第二引数にはフィルターが渡せるので、特定のファイルのみ実行したい場合はフィルターを追加してあげてください。
ファイルアップロード
テスト時は/spec/test_uploads
配下に画像をアップロードする設定を追加
(spec/test_uploads配下の指定は適当でOK)
Paperclipを使用する場合
Paperclip::Attachment.default_options[:path] = \
"#{ Rails.root }/spec/test_uploads/:class/:style.:extention"
Carrierwaveを使用する場合
def store_dir
if Rails.env.test?
"spec/test_uploads/#{model.class.to_s.underscore}
else
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
テストの実行が終わったらアップロードされたファイルを削除する処理を追加。
RSpec.configure do |config|
config.after(:suite) do
FileUtils.rm_rf(Dir["#{ Rails.root }/spec/test_uploads/"])
end
end
gitignoreに、テスト時にアップロードされたファイルを無視する設定を追加
:.gitignore
/spec/test_uploads
各種specファイルの生成
ジェネレーター使用時は不要なテストファイルが作られないように設定
(全部自動で作成されてしまうとファイルが増えてごちゃごちゃするのであまり好きじゃない)
config.generators do |g|
g.test_framework :rspec,
fixtures: true,
view_specs: false,
helper_specs: false,
routing_specs: false,
controller_specs: false,
request_specs: false
g.fixture_replacement :factory_bot, dir: "spec/factories"
end
必要になったらジェネレーター経由でテストファイルを作成
$ rails g rspec:model ◯◯
$ rails g factory_bot:model ◯◯
$ rails g rspec:feature ◯◯
$ rails g rspec:request ◯◯
初期設定一覧
最後に、上記の設定をひとまとめにします。
ごちゃごちゃしてややこしい! という方は、何も考えず下記をコピーすればとりあえずOKです。
config.generators do |g|
g.test_framework :rspec,
fixtures: true,
view_specs: false,
helper_specs: false,
routing_specs: false,
controller_specs: false,
request_specs: false
g.fixture_replacement :factory_bot, dir: "spec/factories"
end
--color
--require spec_helper
--format documentation
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'simplecov'
require 'capybara/rspec'
require 'factory_bot'
require 'shoulda-matchers'
require 'vcr'
require 'spec_helper'
require 'rspec/rails'
# テスト実行前に未実行のmigrationファイルを検知して実行する
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
# support/config配下のファイルを読み込み
Dir[Rails.root.join("spec/support/config/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/helper/*.rb")].each { |f| require f }
RSpec.configure do |config|
# focusタグがあればそれだけ実行、なければ全spec実行
config.filter_run focus: true
config.run_all_when_everything_filtered = true
# ロードするfixtureのパスを指定
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# ディレクトリ構成によってspec typeを自動判別する設定
config.infer_spec_type_from_file_location!
# spec実行後のbacktrace表示を簡素化
config.filter_rails_from_backtrace!
end
require 'simplecov'
SimpleCov.start 'rails'
RSpec.configure do |config|
# focusタグがあればそれだけ実行、なければ全spec実行
config.filter_run focus: true
config.run_all_when_everything_filtered = true
# specをランダム実行
config.order = :random
end
RSpec.configure do |config|
# FactoryBotの呼び出し簡略化
config.include FactoryBot::Syntax::Methods
# springが原因でfactoryが正しく読み込まれないことを防ぐ
config.before :all do
FactoryBot.reload
end
end
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
RSpec.configure do |config|
# database_cleanerの設定
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
RSpec.configure do |config|
# テスト実行後にアップロードされたファイルを削除
config.after(:suite) do
FileUtils.rm_rf(Dir["#{ Rails.root }/spec/test_uploads/"])
end
end
# shoulda-mathcerの設定
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
VCR.configure do |config|
config.cassette_library_dir = "#{ ::Rails.root }/spec/cassettes"
config.hook_into :webmock
config.ignore_localhost = true
config.configure_rspec_metadata!
end
最後に
「こんな設定あるよー」とか「これ便利だよー」みたいなのあれば、教えてほしいです!