對不起,這麼長回答,但是我認爲如果我全部經歷了這個過程,我的思維過程會更加連貫。
由於此問題標記爲TDD,因此我假定您正在編寫方法TDD樣式。如果是的話,你可能要開始:
describe "my_square_function" do
it "Squares a positive number" do
my_square_function(1).should == 1
end
end
有一個失敗的測試,你可以實現my_square_function
如下:
def my_square_function(number)
1
end
現在,測試合格要重構出複製。在這種情況下,代碼和測試之間的重複,即文字1.由於參數帶有測試的值,所以我們可以使用參數來刪除重複。
def my_square_function(number)
number
end
現在,複製已被刪除,測試仍然可以通過,我們可以移動到下一個測試:
describe "my_square_function" do
it "Squares a positive number" do
my_square_function(1).should == 1
end
it "Squares a negative number" do
my_square_function(-1).should == 1
end
end
運行你再次失敗的測試迎來了測試,所以我們使它傳:
def my_square_function(number)
number.abs # of course I probably wouldn't really do this but
# hey, it's an example. :-)
end
現在這個測試通過,它的時間移動到另一個測試:
describe "my_square_function" do
it "Squares a positive number" do
my_square_function(1).should == 1
end
it "Squares a negative number" do
my_square_function(-1).should == 1
end
it "Squares other positive numbers" do
my_square_function(2).should == 4
end
end
在這一點上,你最新的測試將不再通過,所以現在要讓它通過:
def my_square_function(number)
number.abs * number
end
哎呀。這不起作用,它導致我們的負數測試失敗。幸運的是,失敗表明我們回到了無效的確切測試,但我們知道由於「負面」測試而失敗。回到代碼:
def my_square_function(number)
number.abs * number.abs
end
這樣比較好,我們所有的測試都通過了。現在是時候重新構造了。在這裏,我們在abs
的調用中看到一些其他不必要的代碼。我們可以擺脫他們:
def my_square_function(number)
number * number
end
測試仍然通過,我們看到一些與討厭的論點更多的重複。讓我們看看我們是否可以擺脫它:
def my_square_function(number)
number ** 2
end
測試通過,我們不再有這種重複。現在,我們有一個乾淨的實現,讓我們處理nil
情況下一:
describe "my_square_function" do
it "Squares a positive number" do
my_square_function(1).should == 1
end
it "Squares a negative number" do
my_square_function(-1).should == 1
end
it "Squares other positive numbers" do
my_square_function(2).should == 4
end
it "Doesn't try to process 'nil' arguments" do
my_square_function(nil).should == nil
end
end
好了,我們又回到了再次失敗,我們可以繼續前進,實現nil
檢查:
def my_square_function(number)
number ** 2 unless number == nil
end
這測試通過,它很乾淨,所以我們會保持原樣。現在我們回到規範,看看我們有什麼,並驗證我們喜歡我們所看到的:
describe "my_square_function" do
it "Squares a positive number" do
my_square_function(1).should == 1
end
it "Squares a negative number" do
my_square_function(-1).should == 1
end
it "Squares other positive numbers" do
my_square_function(2).should == 4
end
it "Doesn't try to process 'nil' arguments" do
my_square_function(nil).should == nil
end
end
我的第一個傾向是,我們真的描述「平方」的數字,行爲不函數本身,所以我們將改變它:
describe "How to square a number" do
it "Squares a positive number" do
my_square_function(1).should == 1
end
it "Squares a negative number" do
my_square_function(-1).should == 1
end
it "Squares other positive numbers" do
my_square_function(2).should == 4
end
it "Doesn't try to process 'nil' arguments" do
my_square_function(nil).should == nil
end
end
現在,這三個示例名稱在放入該上下文中時會有點鬆動。我將從第一個例子開始,對於方塊1,這似乎有點俗氣。這是我將要減少代碼中的示例數量的選擇。我真的希望這些例子以某種方式變得有趣,否則我不會測試它們。平方1和2之間的區別是無趣的,所以我將刪除第一個例子。它起初是有用的,但不再是。這使得我們有:
describe "How to square a number" do
it "Squares a negative number" do
my_square_function(-1).should == 1
end
it "Squares other positive numbers" do
my_square_function(2).should == 4
end
it "Doesn't try to process 'nil' arguments" do
my_square_function(nil).should == nil
end
end
我要看看接下來的事情就是反面的例子,因爲它涉及到在描述塊的上下文。我要去給它和實例的新描述的其餘部分:
describe "How to square a number" do
it "Squaring a number is simply the number multiplied by itself" do
my_square_function(2).should == 4
end
it "The square of a negative number is positive" do
my_square_function(-1).should == 1
end
it "It is not possible to square a 'nil' value" do
my_square_function(nil).should == nil
end
end
現在,我們已經限制了測試用例的數量最有趣的,我們真的沒有太多要處理用。正如我們上面看到的那樣,如果發現另一個測試案例,我們不希望發生故障,那麼知道故障發生在哪條線路上真是令人高興。通過構建一個場景列表,我們會失去該功能,從而難以調試失敗。現在,我們可以用另一個解決方案中提到的動態生成的it
塊代替示例,但是我們開始失去了我們試圖描述的行爲。因此,總而言之,通過將您的測試場景限制爲僅描述系統有趣特性的場景,您將減少太多場景的需求。在一個更復雜的系統中,有許多場景可能會強調對象模型可能需要另一個外觀。
希望有幫助!
布蘭登
+1爲給出有意義的名稱來測試輸入輸出組合,幫助您瞭解爲什麼需要組合。容易的事情..但很少見。有沒有辦法自定義應該失敗的消息(無需編寫自定義匹配器)? – Gishu
謝謝布蘭登!這是嚴格的TDD的一個很好的解釋。你的方法明確了爲什麼每個數據點都很重要,而不是僅僅在函數中拋出一堆示例。 – brahn
@Gishu我不知道如何在沒有自定義匹配器的情況下自定義消息,雖然它們很容易編寫,所以我不會害怕這樣做。 – bcarlso