44行寫就Ruby單元測試框架

唐尤華發表於2012-03-04

在去年的YOW Melbourne開發者大會上,我參加了一些研習班。這些研習班由@coreyhaines@rains負責,因此TDD(測試驅動開發)成為了主要討論的內容。通常這不是一個問題,但是令人沮喪的是(考慮到這是2010年舉辦的開發者大會),那時上網還不是很方便,我剛裝上linux的筆記本無法下載Rspec。幸運的是幾周前,我決定自己寫一個單元測試框架(因為我有這個能力:)),接著我就有了一個可用的測試框架,問題解決了。但是,這讓我想到一個問題,最少可以用多少程式碼寫成一個可用的單元測試框架

一個最小可用的單元測試

剛開始寫一個單元測試框架的時候程式碼是很少的,但當我想給它加入一些特性時就變得沒有那麼精煉了:) 幸運的是重寫是很容易的。我們真正需要做的是執行下面的程式碼:

正如你看到的,它很像是一個基本的Rspec測試。讓我們寫一些程式碼來執行它。

譯註:RSpec 工具是一個 Ruby 軟體包,可以用它構建有關您的軟體的規範。該規範實際上是一個描述系統行為的測試。

構建一個簡單的框架

首先要做的是使用“describe”來定義一個新的測試。既然我們想要把”describe” block放在任何地方(例如,檔案本身),我們需要對Ruby做一點擴充套件。“puts”函式在Kernel block中,因此可以在任何地方使用(因為Object類包含了Kernel並且Ruby中的每個物件都繼承自Object類),同樣的我們會把describe放到Kernel block中以賦予同樣的能力):

譯註:Ruby block:Ruby語言的block功能類似回撥函式。

正如你看到的,”describe”接收一個用來描述測試的字串和包含了測試程式碼的block。在這裡,我們將測試的程式碼和”describe”分開講解(例如,”it” block)。因此我們建立了Dsl類,用它的parse函式處理待測試的block,結果會產生一個可以執行我們所有測試的物件,但是不要高興得太早。Dsl類看上去是這樣的:

這裡要做的是在Dsl物件的上下文裡對block求值:

我們的Dsl物件有一個”it”函式,同樣也接收一個描述和一個block,這裡和describe block包含的內容完全一致,一切都執行得很好(例如,我們基本上會在幾個函式呼叫時使用”it”函式,每次都傳入一個描述和一個block)。我們還可以在Dsl物件中定義其他的函式,並且這些函式會成為允許在”describe” block中使用的“語言”的一部分)。

在describe block中,”it”函式會為每個”it” block呼叫一次。每次呼叫時,會把輸入的block以測試描述作為鍵值儲存在雜湊表中。完成這些以後,我們只要建立一個Executor物件,可以對我們所有的測試block進行迭代,呼叫它們併產生執行結果。Executor程式碼如下:

我們的executor程式碼非常簡單。輸出”describe” block的描述,然後遍歷所有儲存的”it” block並且在executor物件中執行它們。這麼處理沒有什麼特別原因,但這意味著executor物件同樣也可以包含其他函式,並且可以在”it” block中作為一種“語言”來使用(比如,我們dsl的一部分可以定義為executor的一個函式)。譬如,我們可以在executor上定義下列函式:

這個函式同樣可以在”it” block內部使用,但對於我們這個簡單的測試沒有這個必要。

所以,”it” block會計算並儲存結果,通常結果只是”it” block最後一個語句的返回值(按照常規的Ruby)。這裡,我們希望確保最後一個語句總是返回一個布林值(標明測試通過或失敗),通過它我們可以輸出一些有意義提示。

我們還差最後一步,”should”函式程式碼如下:

每個物件都應當提供自己”should”函式,程式碼如下:

這個函式並沒有真正做什麼工作(僅僅是返回物件本身);它僅僅是一個讓測試讀起來更好的語法。

在這個階段,我們只是將測試計算的結構轉換成一個字串,表明測試結果通過或失敗並輸出。在這個過程中,我們會統計通過或失敗的測試數量,所以可以在最後給出一個總結報告。這就是我們所需要的所有的程式碼,如果我們將他們放到一起,就是下面的44行程式碼:

如果我們“需要”使用這個框架執行最初的那個測試,我們會得到下面輸出結果:

some test

– should be true SUCCESS

– should show that an expression can be true SUCCESS

– should be failing deliberately FAILURE

3 tests, 2 success, 1 failure

太好了!現在,如果你因沒有一個單元測試框架而煩惱並且不想莽撞地寫程式碼,只要花上5分鐘你就可以得到一個能夠助你一臂之力的測試框架。當然,這裡有一些略微誇大;你很快就會想到這裡缺少額外的驗證API、更好的輸出、物件模擬和測試樁等等。然而,我們可以很容易的在精簡的框架上擴充套件其中的一些功能(例如,增加額外的DSL元素)——只消花費很小的努力。如果你不相信我,可以看看bacon ,它只用了幾百行程式碼就完成了Rspec一個精簡版。我編寫的Attest測試框架是另一個很好的例子(這麼說有自賣自誇的嫌疑:P)。這兩者都缺少任何內建的test double 支援,我會在另外一個時間討論如何新增test double支援

譯註:Test Double:在物件程式設計中“自動化單元測試”的專業術語,涵蓋的型別有Test Stub(測試樁)、Mock Object、Test Spy、Fake Object和Dummy Object。

英文原文:Alan Skorkin 編譯:伯樂線上唐尤華

【如需轉載,請標註並保留原文連結、譯文連結和譯者等資訊,謝謝合作!】

相關文章