[Rails]rspecのセットアップから基本の書き方について
はじめに
rpecのセットアップ、モデルspecとシステムspecについてまとめました。
Rspecとは
RSpecは、テストを実行するためのテストフレームワークの一つです。
Railsアプリでは、RSpecを使ってモデル、ビュー、コントローラーなどのコンポーネントをテストすることが一般的です。
種類 | 用途 |
---|---|
ユニットテスト | 個々のコンポーネント(通常はモデル)をテスト |
コントローラテスト | コントローラーアクションの振る舞いをテスト |
統合テスト | アプリケーションの異なる部分が予想通りに連携して動作することをテスト |
システムテスト | ユーザーの視点からアプリケーションの機能をテスト |
ビューテスト | ビューのレンダリングをテスト |
ヘルパーテスト | ビューのヘルパーメソッドの振る舞いをテスト |
メイラーテスト | アプリケーションから送信されるメールの振る舞いをテスト |
ジョブテスト | バックグラウンドジョブ(例: Active Job)の正確性をテスト |
キャッシュテスト | キャッシュ戦略(例: フラグメントキャッシュ)が正常に機能することをテスト |
ルーティングテスト | アプリケーションのルーティングが正しく設定されていることをテスト |
適切なテストの種類を選択し、アプリの要件と機能に合わせてテストを書きましょう。
流れ
- rspecに必要なgemをインストールする
- rspecをインストールする
- モデルspecを作成する
- テストを実行する
- システムspecを作成する
- 共通するコードをモジュール化する
- モデルspecとシステムspecの違い
gemをインストール
group :development, :test do
gem 'byebug', platforms: %i[mri mingw x64_mingw]
gem 'rspec-rails'
gem 'factory_bot_rails'
end
bundle install
factory_bot_rails
(以前はFactoryGirl
とも呼ばれていました)は、Railsアプリのテストで使用されるGemで、モデルのテストデータを簡単に生成されます。
rspecをインストール
bundle exec rails generate rspec:install
Running via Spring preloader in process 65071
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
三つのファイルを作成されましたね。
.rspec
1. テストディスクリプションを表示させる追記します。
# spec/spec_helper.rbを読み込む記述
--require spec_helper
# テストディスクリプションを表示させる
--format documentation
spec/spec_helper.rb
2. 基本的にいじらないです。
# railsを読み込まない
# rubyのクラスやオブジェクトなど
# rspecとruby用
spec/rails_helper.rb
3. FactoryBot
のシンタックスをサポートさせる記述を追加します。
# 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', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# ここからrailsを読み込まれる
# user = FactoryBot.create(:user)
# user = create(:user) # FactoryBotを略す
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
モデルspecを作成する
名前の通り、モデルspecはモデルに対するテストのことです。
モデルはデータベーステーブルと連動し、データベース内のデータの操作やクエリを担当します。
モデルspecは、これらのモデルの振る舞いとデータ操作をテストするために使用されます。
Task
モデルに対してモデルスペックを作成します。
bundle exec rails generate rspec:model task
Running via Spring preloader in process 65269
create spec/models/task_spec.rb
invoke factory_bot
create spec/factories/tasks.rb
spec/models/task_spec.rb
初期のrequire 'rails_helper'
RSpec.describe Task, type: :model do
# pending "add some examples to (or delete) #{__FILE__}"
end
初期のspec/factories/task.rb
FactoryBot.define do
factory :task do
end
end
テストを実行する
# 全てのrspecを実行
bundle exec rspec
# 指定したrspecを実行
bundle exec rspec spec/models/task_spec.rb
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Task add some examples to (or delete) /Users/***/workspace/runteq/rspec_beginner/sample_app_for_rspec/spec/models/task_spec.rb
# Not yet implemented
# ./spec/models/task_spec.rb:4
Finished in 0.00251 seconds (files took 1.16 seconds to load)
1 example, 0 failures, 1 pending
ここまでひと通り実行できましたらセットアップが完了です。
ここからテストコードを書いていきます。
spec/factories/task.rb
にテストデータを作成する
FactoryBot.define do
factory :task do
sequence(:title, "title_1")
# 順序でユニークな値を生成する
sequence(:title) { |n| "title_#{n}" }
content { 'Content' }
status { 'todo' }
deadline { 1.week.from_now }
association :user
end
end
# factoryの名前をモデル名とは別にしたい場合は、以下のようにクラスを指定する
FactoryBot.define do
factory :admin, class: "Task"
…
spec/models/task_spec.rb
にテストコードを作成する
RSpec.describe Task, type: :model do
describe 'validation' do
it 'is valid with all attributes' do
# NG 一つのテスト失敗したら次の実行が中断される
task = Task.new(title: 'New Task', content: 'New Task Content', status: 0)
# 1行に1つずつテストコードを書く
expect(Task.title).to eq 'title_1'
expect(Task.content).to eq 'Content'
# enum status: { todo: 0, doing: 1, done: 2 }
expect(Task.status).to eq 0
# buildを使う
task = build(:task)
expect(task).to be_valid
end
end
end
create
とbuild
の違い
build:データベースに保存されない、rails console
でid
がnil
で分かる。
create:データベースに保存される、rails console
でid
があることで分かる。
マッチャ
# aはbと等しい値に期待する
expect(a).to eq(b)
マッチャ一覧
ここまではモデルspecでした。
続いてシステムspecについてまとめます。
capybaraをインストール
gem 'capybara'
bundle install
Capybaraは、Railsの統合テストやシステムテストなどのコンテキストで使用されます。
Capybaraは、テストケースをシミュレートし、クリック、フォームの入力、ボタンのクリックなど、ユーザーがウェブアプリケーション上で行う操作をシミュレートできます。複数のブラウザドライバーをサポートしており、異なるブラウザでテストを実行できます。
システムspecファイルを作成する
rails generate rspec:system task
Running via Spring preloader in process 98275
create spec/system/tasks_spec.rb
spec/system/tasks_spec.rb
ユーザーをログインしてからタスクを作成までの動きをテストします。
require 'rails_helper'
RSpec.describe 'Tasks', type: :system do
describe 'logged in' do
before { login_as(user) }
it 'allows the user to create a new task' do
visit new_task_path
fill_in 'Title', with: 'Test task'
fill_in 'Content', with: 'Test content'
fill_in 'Deadline', with: DateTime.new(2023, 6, 1, 10, 30)
select 'todo', from: 'Status'
click_button 'Create Task'
expect(page).to have_content('Task was successfully created.')
expect(page).to have_content('Test task')
expect(page).to have_content('Test content')
expect(page).to have_content 'Deadline: 2023/6/1 10:30'
end
end
end
create_list
を使って複数のインスタンスを作成する
crete_list
はFactoryBotで用意されているメソッドです。
作成されたインスタンスは配列になります。
RSpec.describe 'Tasks', type: :system do
describe 'logged in' do
before { login_as(user) }
context 'tasks page' do
it 'show all tasks' do
task_list = create_list(:task, 3)
visit tasks_path
expect(page).to have_content task_list[0].title
expect(page).to have_content task_list[1].title
expect(page).to have_content task_list[2].title
expect(current_path).to eq tasks_path
end
end
end
end
コンソールでFactoryBotを使用したいときは、require 'factory_bot_rails'
を入力しなければ使うことはできません。
require 'factory_bot_rails'
include FactoryBot::Syntax::Methods
特定のテストだけを走らせたい時に
spec_helper.rb
に記述を追加します。
RSpec.configure do |config|
...
# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus
=begin
# Allows RSpec to persist some state between runs in order to support
...
Kernel.srand config.seed
=end
end
実行したいテストにfocus: true
を追加します。
RSpec.describe Task do
it '...' do
# ...
end
it '...', focus: true do
# ...
end
end
spec/support
に入れる
共通するテストーコードをモジュール化にして複数のテストに共通するloginメソッドをmoduleとしてまとめていきます。
spec/rails_helper.rb
の設定を変える
# supportファイルを読み込む
# 25行目にあるコードがコメントアウトされています。コメントアウトを解除します
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
# 読み込むmoduleを追加する
RSpec.configure do |config|
config.include LoginMacros
end
login_support.rb
を作成する
module LoginMacros
def login_as(user)
visit root_path
click_link 'Login'
fill_in 'Email', with: user.email
fill_in 'Password', with: 'password'
click_button 'Login'
end
end
login_as
メソッドを読み込む
テストコードにRSpec.describe 'Users', type: :system do
...
describe 'logged_in' do
before { login_as(user) }
...
モデルspecとシステムspecの違い
モデルspecは、モデルのバリデーション、関連付け、メソッド、スコープなどの動作をテストします。一方、システムspecは、ユーザーがアプリケーションを使用する際のテストするために使用されます。例えば、システムspecでは、ユーザーが特定のアクションを実行した場合の画面遷移、データベースの変更、フラッシュメッセージの表示などをテストします。
モデルspecはモデルの単一のコンポーネントをテストし、システムspecはエンドツーエンドのユーザーシナリオをテストします。
終わりに
rspecのインストールからセットアップまでまとめてみました。
次回では例を見ながら復習していきたいと思います。
Discussion