2009-01-01 136 views
1

我開始並熱愛TDD,但是對紅綠燈概念感到疑惑。理論上我理解確保在通過測試之前可以通過測試失敗的重要性。然而,在實踐中,我發現這種做法有些徒勞無功。紅色綠燈測試的相關性

我覺得如果不實施我打算測試的代碼,我不能正確編寫失敗或通過的測試。例如,如果我編寫測試以顯示DataProvider正在返回DataRow,則需要編寫DAL邏輯以提供有意義的失敗,比空方法更多的NullException或Null返回的失敗,看起來似乎毫無意義,因爲我覺得紅燈應該表明我可以根據我測試的實際邏輯創建一個失敗的測試。

換句話說,如果我只是返回null或false,我正在測試一個函數來測試我的失敗是什麼,真正的紅色光的價值。但是,如果我已經實現了邏輯(這在某種程度上違背了測試的第一範式),我發現我僅僅是爲了測試互斥概念(IsTrue代替IsFalse,或者IsNull代替IsNotNull)獲得紅燈而不是綠燈,然後將它們切換到相反以獲得通行證。

我沒有去理解這個概念,我真的提出這個問題,因爲它是我注意到的,並且想知道如果我做錯了什麼。

編輯

我接受查理·馬丁的答案,因爲它爲我工作最好的,這是不以任何方式暗示,有在其他的答案沒有效力,所有這些都讓我明白一個概念,我是顯然沒有適當地養護

回答

3

認爲它是一種規範。你自問開始「我最終希望能夠做的代碼應該怎麼做?」所以,假設你想寫一個添加自然數的函數。你怎麼知道這是否有效?嗯,你知道2 + 2 = 4,所以你可以寫一個測試(這基本上是蟒蛇,但留下了很多細節,請參見unittest module docs):

def test2plus2(self): 
    assertEquals(addNat(2,2), 4) 

所以在這裏你已經定義了一個規範說「對於自然數a和b,計算a + b」。現在,你知道你需要什麼寫功能

def addNat(a,b): 
    return a+b 

你運行它,它通過測試。但是接下來還有一些你知道的事情。因爲它是自然數只(無論何種原因),你需要添加一個防範自然數:

def testUnnatural(self): 
    failUnlessRaises(AssertionErrof, addNat(-1, 2)) 

現在你已經添加了一個規範,說:「並拋出一個AssertionError如果數字是否定的。」告訴你下一段代碼:

def addNat(a,b): 
    """Version two""" 
    assert((a >= 0) and (b>=0)) 
    return a+b 

現在運行此操作,斷言不會觸發;再次成功。

問題是TDD是一種定義非常明確的詳細規格的方法。對於像「addNat」這樣的東西,他們並不需要,但真正的代碼,特別是在敏捷的世界中,你不需要直觀地瞭解答案。 TDD可以幫助您分揀並處理真實的需求,

9

紅色右邊的價值在於它能夠發現假陽性。它發生在我身上,無論我的實現代碼是什麼,它總是通過測試。正是在這種情況下,紅燈/綠燈測試有所幫助。

在我身上也發生了一些我的測試根本沒有運行,而當我沒有使用紅燈時,我所看到的只是「Build Succeeded」。如果我使用紅燈確保我的測試失敗,那麼當我預計構建失敗時,我會看到構建成功的那一分鐘。

+1

準確無誤。特別是對於更大的測試套件,它往往會發生的事情往往是你忘記添加測試用例。紅色燈光顯示出noop impl確保您的測試實際上正在運行。 – Ole 2009-01-01 15:52:02

1

有幾個激勵的例子,我可以想到爲什麼紅燈是有用的,並幫助我巨大。

  1. 爲了理智,寫一個紅色測試。我確信測試可以驗證我所知道的某些功能尚未實現,但真的很明顯並非如此。

  2. 當你在你的代碼中發現一個錯誤時,你寫了一個失敗的測試來指出這個錯誤。從一開始就進行紅色測試,你很確定你已經知道了,並知道錯誤何時被修復。

可能有一次紅燈沒有用的例子,那就是當你正在編寫測試來填充可用的功能時,它們通常從開始就是綠色的。儘管我會提醒你編寫綠色測試,但可能發生的情況是你必須重新設計類,而不是重複性,這使得一些測試過時。 所有的綠色測試寫作工作沒有任何!

+0

您可以隨時編寫一個測試,在添加之前預期額外的功能。即使它是一個簡單的編譯失敗,至少也是一盞紅燈。 – tvanfosson 2009-01-01 13:40:31

1

我不知道我是否明白自己的觀點,但以下是我如何看待此事。

不用考慮函數返回什麼,更多關於它的作用和它假定的是真實的。

如果我真/假功能是下面的C函數的一些語言版本:

bool isIntPrime(int testInt) 

那麼你要確保一個測試,如果你傳遞一個雙(而不是有一個「有用的」隱性故障就像你在某些語言中可能會遇到的那樣)。

如果你真的找不到'紅燈'的情況,那麼你的'綠燈'基本上沒有意義。如果你真的遇到這種情況,那麼測試可能不值得太多,所以測試該功能/功能有點浪費時間。也許它是如此簡單而強大,它實際上不會失敗?然後寫一堆「綠燈」測試是浪費你的時間。

它有點像白兔想法實驗。 如果我假定所有的兔子都是棕色的,那麼計算棕色兔子對於確立我的主張是否準確無關。 但是,我看到的第一隻白兔證明了我的說法是錯誤的。

這個微不足道的例子有幫助嗎?

1

我總是從我的代碼開始,拋出一個NotImplementedException,儘管有些人會聲稱你應該從未實現該方法開始,並將失敗的編譯爲第一次失敗的測試。對此有一些邏輯,如果你可以在不使用方法的情況下編寫一個測試(並且通過),那麼你就不需要編寫任何代碼。不過,我通常在我的腦海裏這樣做。

編寫了異常拋出代碼後,我繼續爲我正在處理的功能編寫第一個測試,並獲取第一個紅燈(推測)。現在我可以繼續進行TDD - Red-Green-Refactor的規律節奏。不要忘記最後一步。當您的測試通過時重構 - 而不是在編寫代碼來糾正失敗的測試時。

這種方法需要遵守紀律,有時候看起來好像你在做愚蠢的事情,因爲通過第一個測試最簡單的事情就是返回一些硬編碼的數據。堅持,但;這個引導階段相對較短,如果你不跳過它,你可能會發現你編寫的代碼更簡單,更易維護,而不是讓你的解決方案(或者至少骨架)在第一次測試中神奇地變成整體。如果你不是以小幅度增長開發的,那麼你沒有做TDD。

強制免責聲明:不要忘記TDD是關於單元測試。還有其他類型的測試(集成,驗收,加載......),當您開始進行TDD時,其他類型測試的需求不會奇蹟般地消失。