はじめに
RSpec学習中に「letとbeforeなら、letを使ったほうが無難」ということを耳にしました。しっかり理解した上で、letを使いたいと思いletとbeforeの違いを調べたところ、淡白な解説を読むよりも、具体例を示したほうが一目瞭然であることに気づき、備忘録としてまとめることにしました。
そこで、本記事ではuserモデルの単体テスト
を例に、beforeとletを使用した時の挙動の違いを示します。
使用技術
- gem
- devise
- factory_bot
- rspec
準備
- deviseで、ユーザモデルを作成 (https://qiita.com/Hal_mai/items/350c400e8763ce0487a3)
- factory_botで、テストデータを用意
- passwordは、存在チェックのvalidationがかけられているため、敢えて空白を代入し、保存時にエラーが出る前提のuserデータを用意しました。
テストデータ(user)
FactoryBot.define do
factory :user do
name { "User1" }
email { "email@sample.com" }
#validationでエラーが出る様にあえて空白を代入
password { "" }
end
end
beforeを用いた場合のrspec
require 'rails_helper'
RSpec.describe User, type: :model do
#------------beforeを用いた場合-----------
before do
@user = build(:user)
end
#---------------------------------------
#正常系
it "is valid with adequet name, email and password" do
user = @user
expect(user).to be_valid
end
#異常系
it 'is invalid without a name' do
user = @user
user.name = ""
user.valid?
expect(user.errors.messages[:name]).to include("を入力してください")
end
end
letを用いた場合のrspec
require 'rails_helper'
RSpec.describe User, type: :model do
#------------letを用いた場合-----------
let(:user) { build(:user) }
#---------------------------------------
#正常系
it "is valid with adequet name, email and password" do
user = @user
expect(user).to be_valid
end
#異常系
it 'is invalid without a name' do
user = @user
user.name = ""
user.valid?
expect(user.errors.messages[:name]).to include("を入力してください")
end
end
テスト結果
beforeを用いた場合
F.
Failures:
1) User is valid with adequet name, email and password
Failure/Error: expect(user).to be_valid
expected #<User id: nil, email: "email1@sample.com", name: "User1", created_at: nil, updated_at: nil> to be valid, but got errors: パスワードを入力してください
# ./spec/models/user_spec.rb:13:in `block (2 levels) in <top (required)>'
Finished in 0.99262 seconds (files took 11.68 seconds to load)
2 examples, 1 failure
letを用いた場合
FF
Failures:
1) User is valid with adequet name, email and password
Failure/Error: expect(user).to be_valid
NoMethodError:
undefined method `valid?' for nil:NilClass
# ./spec/models/user_spec.rb:11:in `block (2 levels) in <top (required)>'
2) User is invalid without a name
Failure/Error: user.name = ""
NoMethodError:
undefined method `name=' for nil:NilClass
# ./spec/models/user_spec.rb:17:in `block (2 levels) in <top (required)>'
Finished in 0.16009 seconds (files took 12.2 seconds to load)
2 examples, 2 failures
beforeを用いた場合は、2件中1件のテストケースが成功してしまっています。また、エラーの原因がコンソールから分かりません。
一方、letを用いた場合は、2件ともテストケースが失敗しており、かつNoMethodError
でname
に異常があることが一目瞭然です。
参考文献
- RSpecのletを使うのはどんなときか?(翻訳)
https://qiita.com/jnchito/items/cdd9eef2ed193267c651