昨天實作了把 example
給拆開,並且讓整個測試更具備邏輯。
今天要讓程式碼更乾淨,解決一些不斷重複的程式碼,在我剛開始學習時,覺得多打一兩行也還好吧。
到現在要我打移動距離去改三個字都讓我覺得痛苦,如果今天放大成 100 倍呢?我相信如果一個小小的改動要連續做一百次,做起來真的是很煩!
Before hooks
下面是昨天實作的測試,接著我們要來重構它。
重構的要點在於減少不必要的程式碼,加速效能,讓他看起來更舒服!
RSpec.describe Burger do
it "has cheese" do
burger = Burger.new('Beef', 'Cheddar')
expect(burger.cheese).to eq('Cheddar')
end
it "has meat" do
burger = Burger.new('Beef', 'Cheddar')
expect(burger.meat).to eq('Beef')
end
end
這段程式碼,最需要改動的地方就在於第 3, 8 行,因為是完全一模一樣的程式碼,所以可以使用我們要介紹的 Before hooks
來解決這件事情!
先上完成的程式碼:
RSpec.describe Burger do
before do
@burger = Burger.new('Beef', 'Cheddar')
end
it "has cheese" do
expect(@burger.cheese).to eq('Cheddar')
end
it "has meat" do
expect(@burger.meat).to eq('Beef')
end
end
上面的程式碼會得到 PASS!
接著來解釋這到底是什麼作用,首先這個 before
的原型應該是這樣:
before(:example) do
...
end
意思就是指,在每一個 example
前,做這件事情!應該是一個很好懂的概念,也非常實用,當然裡面的 :example
可以做改變,這是更進階的寫法,之後會再提到!
所以完全不寫參數給他的話,他就會自動的認定要在每一個 example
前執行 block 裡面的內容!
這邊示範為什麼要提到每一個 example
呢?
試試看在 before
的 block 裡面加上一段會印出來的文字。
RSpec.describe Burger do
before do
p 'Hello, this is before hooks'
end
it "has cheese" do
burger = Burger.new('Beef', 'Cheddar')
expect(burger.cheese).to eq('Cheddar')
end
it "has meat" do
burger = Burger.new('Beef', 'Cheddar')
expect(burger.meat).to eq('Beef')
end
end
接著來看測試的輸出:
很明顯地,它在每一個 example
前執行!
所以在先前的測試中,在進入每一個 example
前,都會有一個實體變數 @burger
產生並一同加入 example
裡面!
那至於為什麼要使用 @burger
而不是 burger
呢?
這就牽扯到 Ruby 變數的作用域問題,我們不深入的討論這件事,但大致上來說,可以把一個 block 想像成一個新的領地,若你不加上 @
這個符號成為實體變數,是沒有穿梭 block 的能力!
用原始的 Ruby Method
上面的方法是 RSpec 提供給的方法,但 RSpec 是可以直接寫 Ruby Code 的,所以我們動動小腦筋,要怎麼做到和 before hook
一樣的效果呢?
上代碼:
RSpec.describe Burger do
def burger
Burger.new('Beef', 'Cheddar')
end
it "has cheese" do
expect(burger.cheese).to eq('Cheddar')
end
it "has meat" do
expect(burger.meat).to eq('Beef')
end
end
這樣得程式碼也會過關喔,可以自己執行測試看看!
對於不熟悉 Ruby 的人一定想說你在寫三小… 但這真的會動,雖然不會有人這樣寫,因為後續會造成特殊的問題,但這也是一個方法啦,如果你確定你的數值不會有任何的改變,還是用 before hooks
吧
它在讀取到第 7 行的時候呼叫了 burger
這個定義的方法,而在 Ruby 中最後一行可以不用寫 return
, 所以它會自動的 return 一個 burger 物件。
所以我們可以這樣看第 7 行
expect(Burger.new('Beef', 'Cheddar').cheese).to eq('Cheddar')
這是它會動的原因,但不要這樣寫,只是稍微提一下!因為這樣真的會有問題~
結語
今天介紹了減少程式碼的好方法 before
,這將會在之後的 RSpec 生涯中偶爾地出現!
什麼?你說這才偶爾,那幹嘛學呢?
總是什麼方法都要看過才好啊!才知道什麼是好,什麼是不好,像今天示範的用原始 Ruby 寫的方法,也會動啊!
但會遇到變動性的問題,所以明天將會介紹在 RSpec 裡面,遇到變動性問題該怎麼解決?以及最強大的方法 let
將會在明天一併介紹