背景
本格的に1人でテストコードを書くとなった際に、セットアップやファイルは何を指しているなど色々と全くわかっていなかったのでその備忘録。
セットアップ
■rspec
のセットアップ
基本的に公式のREADMEを参考に進めていく。
Gemfile
の開発環境とテスト環境にrspec-rails
を記入していく。
group :development, :test do
gem 'rspec-rails'
end
厳密には開発環境にはrspec-rails
は入れなくても良いが、その場合にはRAILS_ENV=test
にする必要がある。
bundle install
後、ターミナルにてrails g rspec:install
を実行。
$ bundle install
$ rails g rspec:install
create .rspec
create spec
create spec / spec_helper.rb
create spec / rails_helper.rb
■factory_bot
のセットアップ
rails
のテストでmodel
を扱うのに便利なFactoryBot
。
設定をしておくとuser = create(:user)
のような形でDB登録やモデルのビルドができる。
group :development, :test do
gem 'factory_bot_rails'
end
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
#このコードを加えることで、以下のようにrspecのテストコード中でFactory_botのメソッドを使用する際に、クラス名の指定を省略できるようになる
end
# 通常FactoryBotをつけないと、メソッドを呼べない
user = FactoryBot.create(:user)
#設定を追加することで、FactoryBotの記述が省略できる。
user = create(:user)
.rspecファイル
に**--require spec_helper
**の一文が入っていなければ追記。rails_helper
の読み込みを行っている。
--require spec_helper
--format documentation
#format documentationを入れると、RSpecで表示の出力を綺麗にすることができる
ファイルの作成
今回はタスク投稿できるアプリで、task
とuser
のモデルテストをしていく。
まずターミナルにてtask
とuser
のモデルのテストファイルを作成する。
$ rails g rspec:model task
create spec/models/task_spec.rb
invoke factory_bot
create spec/factories/tasks.rb
$ rails g rspec:model user
create spec/models/user_spec.rb
invoke factory_bot
create spec/factories/users.rb
テストデータ作成
まずはfactories/users.rb
にてテストユーザーの作成をしていく。
FactoryBot.define do
factory :user do
sequence(:email) {|n| "user_#{n}@example.com"}
#↑これが呼び出されるたびに、nの部分に数字が一つずつ増えて入るため、一意性が保たれる
password {'password'}
password_confirmation {'password'}
end
end
次にfactories/tasks.rb
にてテストタスクの作成。
FactoryBot.define do
factory :task do
sequence(:title, "title_1")
#"title_1"とすることで、"title_◯"と数字が増えていく。
#メール同様一意性が保たれる
content {'content'}
status {:todo}
deadline {1.week.from_now}
association :user
end
end
association :user
例えば下記のようなlet
を使ったコードがあったとする。
RSpec.describe Task, type: :model do
let(:user) { FactoryBot.create(:user) }
let(:task) { FactoryBot.create(:task,user: user) }
end
task
を作成する際に、user
も必要なため、user: user
と記述する必要があるが、association :user
をファクトリに書いておくと、コードがとてもスッキリする。(has_manyとbelongs_toの関係がuserオブジェクト
を自動的に作成してくれる。)
RSpec.describe Task, type: :model do
let(:user) { create(:user) }
let(:task) { create(:task,user: user) }
end
↓このように省略できる
RSpec.describe Task, type: :model do
let(:task) { create(user) }
end
テスト内容ファイル解説
models/task_spec.rb
を例に、ファイル作成時にデフォルトで下記の文言が入っている。この解説を先にする。
require 'rails_helper'
#spec/rails_helper.rb を読み込む為の設定
RSpec.describe Task, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
RSpec.describe Task, type: :model do ~ end
この間に記入したものが、task
のRSpec
を書く宣言をしている。
pending "add some examples to (or delete) #{__FILE__}"
pending
というのは保留の意味で、実際にテストを書いていく際にまだテストをせずスキップさせたい際に使われる。
つまりここでは、 "add some examples to (or delete) #{FILE}" というテスト(仮)を保留としている。
このテンプレートが書かれたModelSpec
でテスト実行をすると下記が出てくる。
1 example, 0 failures, 1 pending
・1 example
1つのテストがある
・0 failures
失敗したテストは0だった
・1 pending
1つ保留のテストがあった
基本的にデフォルトのpending
は要らないので、コメントアウトしておくなり、削除しておくなりしておく。
テスト内容作成
記入の流れ
specファイル
ではほとんど、describe
/context
/it
を用いて記述する。
・describe
にはテスト対象の「クラス」や「メソッド」などを記入。
・context
にはテストしようとしている状況「ログイン中のユーザーの場合」などを記入。
・it
は期待する結果を記入。
大まかな記入の流れは下記。
describe "バリデーションについて" do
context "姓名が設定されている場合 "
it "姓名が表示されること" do
# ...
end
context "姓名が記入されていない場合"
it "姓名を入力してくださいとエラーが出るようにする" do
# ...
end
context "..."
it "..." do
end
.
.
end
実際に記入する際に使うマッチャ(matcher)について
マッチャ(matcher)は「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」のこと。
describe "四則演算" do
context "足し算"
it "1+2の合計は3になること" do
expect(1 + 2).to eq 3
end
end
上記のコードを例に見てみると、直感的に内容が分かりやすくなっている。
expect(1 + 2).to eq 3
expect
は期待するという意味。
to
は、であることを示す。
eq
はイコールなので、日本語に当てはめると
(1+2)イコール3となることを期待します
となり、1+2の合計は3になること
と同義になる。
この**eq
**の部分がマッチャ(matcher)に該当し、他にも種類がある。
to
はマッチャではなくRSpec
のメソッド。「〜ではないこと」はnot_to(to_notでも可)
と表記できる。
よく使うマッチャを表記しておく。
・空であることを望んでいる
expect(user).to be_empty
# user.empty? が true になればパスする
・存在することを望んでいる
expect(user).to be_valid
# user.valid? が true になればパスする
・存在しないことを望んでいる/not_to(to_not)を使わない記入法
expect(user).to be_invalid
# user.invalid? が true になればパスする
テスト作成
実際にtask
のテストを記入する。
require 'rails_helper'
RSpec.describe Task, type: :model do
describe 'validaton' do
it 'タスクを問題なく作成' do
task = build(:task)
expect(task).to be_valid
expect(task.errors).to be_empty
end
it 'タイトル空欄は無効' do
task = build(:task,title: "")
expect(task).to be_invalid
expect(task.errors[:title]).to eq ["can't be blank"]
end
it 'ステータス空欄は無効' do
task_without_status = build(:task,status: nil)
expect(task_without_status).to be_invalid
expect(task_without_status.errors[:status]).to eq ["can't be blank"]
end
it '同一タイトルは無効' do
task = create(:task)
task2 = build(:task, title: task.title)
expect(task2).to be_invalid
expect(task2.errors[:title]).to eq ["has already been taken"]
end
it '別タイトルは有効' do
task = create(:task)
task3 = build(:task,title: "another title")
expect(task3).to be_valid
expect(task3.errors).to be_empty
end
end
end
task = create(:task)
とtask = build(:task)
の違いは、
create
だと実際にテーブルのデータが保存され、
build
ではテーブルにデータが保存されずにテストを実行してくれる。
参考記事
RailsにRSpecを導入する方法とやさしい解説
【Rails】factory_botの使い方メモ
RailsアプリへのRspecとFactory_botの導入手順
rspecを読みやすくメンテしやすく書くために
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」
FactoryBotのassociationとは
【FactoryBot】associationの使い方