昨天介紹了 before 之後今天就可以直接來看 let 摟!
let => 被呼叫才會執行賦值let! => 等同於before(:each)
describe '#change_name' do
let(:user) { User.create!(name: 'test') }
it 'no user' do
expect(User.count).to eq(0)
end
it 'one user' do
user
expect(User.count).to eq(1)
end
end
跑測試會發現通過了! 就能證明 let 在沒有呼叫時是不會做的那麼如果換成 let! 呢?
$ rspec spec/models/user_spec.rb
.F.
Failures:
1) User#change_name no user
Failure/Error: expect(User.count).to eq(0)
expected: 0
got: 1
(compared using ==)
# ./spec/models/user_spec.rb:11:in `block (3 levels) in <top (required)>'
Finished in 0.11105 seconds (files took 3.9 seconds to load)
3 examples, 1 failure
這時會發現就算沒有呼叫user 在第一個 it 還是做了 User.create!(name: 'test') 也就應證了 let! 其實就是 before(:each) 的概念
至於第二個測試沒有錯是在於 它已經做過了 let!(:user) 當然在呼叫一次一樣的實體並不會在重新 create
let 最常拿來與 FactoryBot 搭配使用 之後範例也都會使用到 let 來做測試現在先簡單介紹
subject 在測試不同 type 時預設給的值都不一樣
# type: :model
subject = #<User id: nil, created_at: nil, updated_at: nil, name: nil, email: nil, phone: nil>
# type: :controller
subject = #<UserController:0x007f842e3f58b8>
subject 其實等同於 let 差異性就是 subject 是可以做隱性調用,過去是拿來與 should 一起搭配 但現在更主流是主動式的 expect ,比起should 更好閱讀!
describe 'validations' do
it { should validate_presence_of(:name) }
# 等於 it { subject.should validate_presence_of(:name) }
end
當然在各個不同的 describe 之間也能更改 subject
require 'rails_helper'
describe 'validations' do
it { should validate_presence_of(:name) }
end
describe '#change_name' do
subject { User.create!(name: 'test').change_name('') }
it { should eq(false) }
end
更詳細的解說可以看這篇 Rspec 中 let / let!(驚嘆號) / Instance variables / subject 的用法與差異
在測試之中,我更推薦使用 let。如果用到實體變數的話怕會造成測試之間因為共同的實體變數而出錯 在測試與測試之間最好不要進行任何干擾才能最精準的測試。除非是逼不得已因為有時候使用before(:all)的效能比起let會更好,這時候就要判斷使用before(:all)會不會有干擾了...
明天就來介紹 Database Cleaner 吧!