了解 TDD 後,會開始介紹 RSpec 的基本語法,這些語法將會伴隨整個使用 RSpec 寫測試的過程
describe method
假設今天要開一間漢堡店,那就要來想像漢堡店的運作模式!
首先在 Spec 檔案裡面加入一個 burger_spec.rb
的檔案!
這樣就可以開始來寫測試了。
Rspec.describe 'Burger' do
...
end
這個 describe
呢,主要在解釋測試的主要目標是什麼?( 這邊是測試漢堡這個類別 ),而這邊也有一個 Ruby 小小的慣例,就是可以省略 ( ) 小括號,所以原本應該是這樣…
RSpec.describe('Burger') do
...
end
從這邊可以了解到,describe
應該是 RSpec 的類別方法!
接著 describe
也同時要接收一個 Ruby 的 block 用 do..end
的方式接在後面。
寫到這邊,已經完成了一個 example group
,直翻的話叫做:例子的集合???
也就是提供了一個 example
的地基,在這裡面可以放入許多的小 example
,在 RSpec 的世界中,每一個所期待的事件都是一個 example
,而 describe
在做的事情就組織著這些小東西!
it method
上面的例子中,建立了一個 example group
,而在這個 it
這個語法裡,要建立單一一個測試,也就是一個小的 example
。
回到上面的例子,想了一下,希望漢堡要有種類,就拿坊間常見的漢堡來做例子吧!
RSpec.describe 'Burger' do
it 'has a type' do
...
end
end
可以看到寫了一個 it
,這也是一種方法,只能存活在 example group
裡面,而後面p會接一段字串來描述這個測試的目的是什麼?然後再接一個 block。
這個 it
方法最大的價值就在於,能夠讓一個測試變得易讀,然後清楚地描述這個行為是什麼。
描述有一個重點,就是不要描述實作細節,不需要寫是用 Array 來存放,或用 Hash,諸如此類的東西,不需要被特別的提出來!只需要專注在這個測試的行為是什麼?
像剛剛寫的就很明確,一個漢堡應該要有種類,非常的易讀!
這主要是 it
這個方法的目的,描述一個測試的行為,不是結果喔!結果會有另一個方法來執行,但這邊的目的在於 描述物件的行為
expect & eq methods
接著繼續來開發漢堡店,還記得開發的需求是說 漢堡需要有不同的種類,就著就來想像一下,需要做出漢堡的實體,然後給予他種類,並通過測試。
上述也算是一個 TDD 的迴圈喔!還記得嗎?現在在根據需求並使其通過測試!
RSpec.describe 'Burger' do
it 'has a type' do
burger = Burger.new('Big mac')
...
end
end
這邊建立了一個 burger
的實體,便給予他種類的名稱 Big mac
,接著需要測試它,所以會用到一個叫做 expect
的方法。
expect
這個方法接收的參數是要測試的對象,也就是剛剛做出來的 burger
。
要記得這次測試的目的是,希望他有一個種類,所以在 expect
的後方寫的是 burger.type
並希望得到我們要的答案!
RSpec.describe 'Burger' do
it 'has a type' do
burger = Burger.new('Big mac')
expect(burger.type)....
end
end
接著繼續地寫下去:
RSpec.describe 'Burger' do
it 'has a type' do
burger = Burger.new('Big mac')
expect(burger.type).to eq('Big mac')
end
end
專注在第四行上面,如果用我破爛的英文來翻譯的話,會是
期待漢堡的種類等於
Big mac
應該是還蠻容易懂的吧,雖然不是文法很精確的英文,但我覺得很足夠了
接著重新地整理一下我們寫了一些什麼,以及個別有什麼用途。
首先是 expect
,它是一個 RSpec 提供的方法,可以接受物件、物件的屬性、方法、甚至是基本的 Ruby 運算式,而這個 expect
方法會針對你傳入的參數進行運算,並且回傳一個物件。
#<RSpec::Expectations::ExpectationTarget:0x00007fb81f240278 @target="Big mac">
若是把 expect(burger.type)
印出來就會長的像上面一樣,是一個物件的概念,以及具備 @target
這個實體變數 ( @target 會是主要接受測試的物件! )
而這個物件會擁有 to
這個實體方法,這個 to
方法呢,需要接受一個叫做 matcher
的物件,那這個物件是怎麼產生的?
就在於後面寫入的 eq('Big mac')
這個方法也會產生一個物件
#<RSpec::Matchers::BuiltIn::Eq:0x00007fdb92afa3f0 @expected="Big mac">
具有一個 @expected
的實體變數!
而 to
這個方法呢,簡單的來說就是比對這兩個物件的實體變數,回傳成功或是失敗,當然實際上更複雜一點,有針對許多的情況來做 raise error 但基本邏輯就是這樣進行的,所以若是沒有傳入 matcher
這個例子就沒有辦法完成!
OK,所以到這邊已經寫完了一個最基本的測試了!接著對著終端機輸入 rspec spec/burger_spec.rb
會看到下圖:
竟然沒有通過?如果閱讀過 昨天的文章 的人就大概能知道錯誤是在哪裡發生的,而明天開始將會一步一步地讀懂錯誤的訊息並且使其通過測試
畢竟理解錯誤訊息也是工程師很重要的一項技能!
結語
今天學會了整個 RSpec 的主架構,從利用 describe
框出一個區塊讓我們寫小的 example
再到使用 it
來敘述測試的行為,以及利用 expect&eq
來進行比對測試!
這樣可以算是一個 unit test 囉!運用這幾個方法,相信一般的測試也難不倒你了,但可惜的是最後遇到了錯誤訊息,沒關係!明天來解決它。