2009-02-13 45 views
4

我是TDD的忠實粉絲,現在用於我的絕大多數開發。然而,我經常遇到的一種情況是,從來沒有找到我認爲是「好」的答案,就像下面的(做作的)例子。開發TDD接口

假設我有一個接口,這樣的(用Java編寫,但實際上,這適用於任何面向對象的語言):

public interface PathFinder { 
    GraphNode[] getShortestPath(GraphNode start, GraphNode goal); 

    int getShortestPathLength(GraphNode start, GraphNode goal); 
} 

現在,假設我要創建此接口的三種實現。我們稱它們爲DijkstraPathFinder,DepthFirstPathFinderAStarPathFinder

問題是,我如何使用TDD開發這三種實現?他們的公共接口將是相同的,並且,大概我會爲每個接口編寫相同的測試,因爲getShortestPath()和getShortestPathLength()的結果應該在所有三種實現中保持一致。

我的選擇似乎是:

  1. 寫一組針對PathFinder測試,我編寫了第一個實現。然後寫另外兩個實現「盲」,並確保他們通過了PathFinder測試。這看起來不對,因爲我沒有使用TDD開發第二個實現類。

  2. 以測試優先的方式開發每個實現類。這看起來不對,因爲我會爲每個班級編寫相同的測試。

  3. 結合以上兩種技術;現在我有一套針對接口的測試和一套針對每個實現類的測試,這很好,但測試都是一樣的,這並不好。

這似乎是一個相當常見的情況,特別是在實施策略模式時,當然實現之間的差異可能不僅僅是時間複雜性。其他人如何處理這種情況?有沒有一種針對我不知道的界面進行測試優先開發的模式?

回答

3

您編寫接口測試來鍛鍊接口,並且爲實際實現編寫更詳細的測試。 Interface-based design談了一點關於你的單元測試應該爲該接口形成一種「契約」規範的事實。也許當Spec#出來時,會有支持這種方式的語言支持。

在這個特殊情況下,這是一個嚴格的策略實施,接口測試就足夠了。在其他情況下,如果接口是實現功能的一個子集,則可以對接口和實現進行測試。例如,考慮一個實現3個接口的類。

編輯:這很有用,所以當你添加接口的另一個實現的道路,你已經有測試驗證類實現接口的合同正確。這可以像ISortingStrategy一樣適用於像IDisposable這樣廣泛的內容。

+0

我仍然有問題。我採取了測試駕駛兩個相同類的方法,然後重構爲一個通用接口。在添加第三個類時,我剪切了n-pasted測試,並通過將接口添加到類中來進行每次編譯並轉爲綠色。如果我的cut-n-pasting出錯了,這很容易出錯,但那不是我的問題。 現在我處於可以使用測試向一個類的接口添加功能的情況,但是該功能在其他類中將沒有測試來支持它。我必須記住要複製測試。這看起來不正確? – tenpn 2009-08-27 12:03:16

2

有什麼不妥針對接口編寫測試和重用他們每個實現的,例如 -

public class TestPathFinder : TestClass 
{ 
    public IPathFinder _pathFinder; 
    public IGraphNode _startNode; 
    public IGraphNode _goalNode; 

    public TestPathFinder() : this(null,null,null) { } 
    public TestPathFinder(IPathFinder ipf, 
     IGraphNode start, IGraphNode goal) : base() 
    { 
     _pathFinder = ipf; 
     _startNode = start; 
     _goalNode = goal; 
    } 
} 

TestPathFinder tpfDijkstra = new TestPathFinder(
    new DijkstraPathFinder(), n1, nN); 
tpfDijkstra.RunTests(); 

//etc. - factory optional 

我認爲,這是最少的努力的解決方案,這是非常符合敏捷/ TDD原則。

1

我不介意重複使用測試代碼作爲具有類似功能的新測試的模板。根據測試中的特定類別,您可能需要使用不同的模擬對象和期望重新修改它們。至少你必須重構它們來使用新的實現。不過,我會遵循TDD方法進行一項測試,對新類進行重新編譯,然後編寫代碼以通過該測試。不過,這可能需要更多的紀律,因爲你已經有一個實現在你的背後,無疑會受到你已經編寫的代碼的影響。

2

我會沒有問題與選項1,並記住,重構是TDD的一部分,它通常在重構階段,你移動到設計模式,如戰略,所以我不會感到不好這樣做不會寫新的測試。

如果要測試每個PathFinder impl的實現特定細節,可以考慮傳遞模擬GraphNodes,它們以某種方式能夠幫助聲明實現的Dijkstrass或DepthFirst-ness等。 (也許這些模擬的GraphNodes可以記錄它們是如何遍歷的,或者以某種方式衡量性能。)也許這是測試過度殺傷,但如果你知道你的系統由於某種原因需要這三種不同的策略,那麼測試可能會很好來證明爲什麼 - 否則爲什麼不選擇一個實現並將其他人拋棄?

1

這看起來不對,因爲我是 沒有使用TDD開發第二個 兩個實現類。

當然可以。

首先評論所有的測試,但一個。當你進行測試通過重構或取消註釋另一個測試。

Jtf