RSpecについてちゃんと学習してこなかったので、今回しっかり腰を据えて復習しました。
使った教材は次の3冊です。
- Everyday Rails - RSpecによるRailsテスト入門
- Effective Testing with RSpec 3: Build Ruby Apps with Confidence
- Rails Testing Handbook
以下では、そこで学んだことを整理します。
RSpecのテスト設計
RSpec の基本的なディレクトリ構造は公式ドキュメントで示されています。
基本的には次のような構成になります。
spec
├── factories # テスト生成
├── features # インテグレーション/アクセプタンス
├── helpers # ユニット
├── models # ユニット
├── rails_helper.rb # 設定ファイル
├── requests # インテグレーション/アクセプタンス
├── services # ユニット
└── spec_helper.rb # 設定ファイル
最近は Controller specs の代わりに、より広範囲をテストする Request specs が用いられるのが主流です(両者の違いについてはこちら)。
RSpec チームからも Request specs を使うことが推奨されています(2016年:詳細はこちら)。
For new Rails apps: we don't recommend adding the rails-controller-testing gem to your application. The official recommendation of the Rails team and the RSpec core team is to write request specs instead.
また、ビジネスロジックをサービスオブジェクトに切り出す場合などは、他のユニットテストと同様に、rspec/services
配下にテストコードを書いていきます。
RSpecの初期設定
新規にRailsアプリを作る場合の初期設定について整理します。
gem インストール
まずは RSpec のインストールです。
group :development, :test do
gem 'rspec-rails', '~> 3.6.0'
end
$ bundle install
次のコマンドで RSpec のファイルがもろもろ生成されます。
$ rails g rspec:install
.rspec
好みですが、見やすいフォーマットで表示させるために .rspec ファイルを次のように編集します。
--require spec_helper
--format documentation
Spring
Springを使ってテスト環境の立ち上げ速度を上げます。
gem をインストールします。
group :development do
gem 'spring-commands-rspec'
end
$ bundle install
binstub を使えるようにします。
$ bundle exec spring binstub rspec
これで次のコマンドで RSpec を実行できるようになりました。
$ bin/rspec
config/application.rb
rails generate コマンドで自動生成されるファイル種類を調整します。
config.generators do |g|
g.test_framework :rspec,
view_specs: false,
helper_specs: false,
routing_specs: false
end
spec/rails_helper.rb
次の行のコメントアウトを取り、spec/support 配下にあるRSpecの設定ファイルが読み込まれるようにします。
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
FactoryBot
テストデータ生成用のツール FactoryBot をインストールします。
group :development, :test do
gem 'factory_bot_rails', '~> 4.10.0'
end
$ bundle install
FactoryBotのコードを spec/factories 配下に書いていきます。
テストコードから FactoryBot
を省略してデータ生成できるように設定ファイルを追加します。
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
これで FactoryBot.create(:user)
ではなく、 create(:user)
と書くだけでデータ生成できるようになりました。
ダミーデータ生成用の設定
テストデータ生成用のツール Faker と ForgeryJa をインストールします。
group :development, :test do
gem 'faker', git: 'https://github.com/stympy/faker.git', branch: 'master'
gem 'forgery_ja'
end
$ bundle install
これらを使えば、FactoryBot で任意のダミーデータをセットできます。(Faker は英数字用、ForgeryJa は日本語用です。)
require 'faker'
require 'forgery_ja'
FactoryBot.define do
factory :some_model do
name { ForgeryJa(:name).full_name }
name_kana { ForgeryJa(:name).full_name(to: ForgeryJa::KANA) }
zip_code { Faker::Number.number(digits: 7).to_s }
code { Faker::Lorem.characters(number: 20) }
sex { Faker::Number.between(from: 0, to: 2) }
birthday { Faker::Date.birthday(min_age: 18, max_age: 65)
phone { Faker::Number.number(digits: Faker::Number.between(from: 10, to: 11)) }
email { ForgeryJa('email').address }
end
end
Devise用の設定
Requset specs でログイン周りのDeviseヘルパーが利用できるように、設定ファイルを追加します。
module RequestSpecHelper
include Warden::Test::Helpers
def self.included(base)
base.before(:each) { Warden.test_mode! }
base.after(:each) { Warden.test_reset! }
end
def sign_in(resource)
login_as(resource, scope: warden_scope(resource))
end
def sign_out(resource)
logout(warden_scope(resource))
end
private
def warden_scope(resource)
resource.class.name.underscore.to_sym
end
end
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include RequestSpecHelper, type: :request
end
Capybara
Feature specs 用のツールとして Capybara をインストールします。
group :test do
gem 'capybara', '~> 2.15.4'
gem 'webdrivers'
gem 'launchy', '~> 2.4.3'
end
webdrivers
は、JSの挙動を含めたテストをする際に必要です。launchy
は Feature specs のデバッグツールで、テストが失敗した場合にブラウザがどのような状態だったか、教えてくれます。
$ bundle install
Capybara が読み込まれるように設定ファイルを編集します。
# Add additional requires below this line. Rails is not loaded until this point!
require 'capybara/rspec'
Capybara で利用するウェブドライバーを指定します。
Capybara.javascript_driver = :selenium_chrome
ヘッドレスブラウザとして動かしたい場合は、次のように書きます。
require 'capybara/rspec'
require 'selenium-webdriver'
Capybara.register_driver :headless_chrome do |app|
opts = {
browser: :chrome,
desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
chrome_options: { args: %w[headless disable-gpu window-size=1680,1050 start-maximized] }
)
}
Capybara::Selenium::Driver.new(app, opts)
end
Capybara.javascript_driver = :headless_chrome
基本的なコマンド
RSpecの基本的なコマンドは次のとおりです。
全テストの実行
$ bin/rspec
特定ファイルのテスト
$ bin/rspec spec/unit
処理速度の遅いテストの検出
$ bin/rspec —-profile 2
キーワードが含まれるテストのみ実行
$ bin/rspec -e milk -fd
-e: example
-fd: full_description
- case-sensitive
失敗したテストだけ再実行
$ bin/rspec —-only-failure
便利なタグ
RSpec のテストコードで便利なタグを整理します。
aggregate_failures
デフォルトでは、テスト内でエラーが1つ発生すると、後続のエラーはメッセージに表示されません。
:aggregate_failures
タグをつけると、発生するエラーを全て同時に確認できます。
RSpec.describe Ledger, :aggregate_failures do
# your test code
end
このタグを使ってアサーションをネストさせることもできます。
describe 'GET /your_root' do
it 'responds successfully' do
get your_root_path
aggregate_failures do
expect(response).to be_success
expect(response).to have_http_status '200'
end
end
end
focus
次のコマンドでテストを実行時すると focus: true
タグがついてるテストのみ実行されます。
describe 'SOME TEST', focus: true do
it 'does something' do
expect(1).to eq(1)
end
end
$ bin/rspec --tag focus
各項目の頭に f
をつけることでも、focus: true
を指定できます。例えば、fcontext
fit
fdescribe
のような具合です。
fdescribe 'SOME TEST' do
it 'does something' do
expect(1).to eq(1)
end
end
また、設定ファイル次のようにいじれば、bin/rspec
コマンドだけで focus: true
のみ(なければ全テスト)が実行されるようになります。
RSpec.configure do |config|
config.filter_run_when_matching( focus: true )
end