2012-10-17 66 views
4

我正在使用TDD開展我的第一個項目,並且在繼承方面遇到了一些磚牆問題。TDD和繼承

例如,如果我有這樣的事情

public interface IComponent 
{ 
    void MethodA(); 

    void MethodB(); 
} 

public class Component : IComponent 
{ 
    public virtual void MethodA() 
    { 
     // Do something 
    } 

    public virtual void MethodB() 
    { 
     // Do something 
    } 
} 

public class ExtendedComponent : Component 
{ 
    public override void MethodA() 
    { 
     base.MethodA(); 

     // Do something else 
    } 
} 

那麼我不能孤立測試ExtendedComponent因爲它依賴於組件。

然而,如果使用組合物來創建ExtendedComponent這樣

public class ExtendedComponent : IComponent 
{ 
    private readonly IComponent _component; 

    public ComponentB(IComponent component) 
    { 
     _component = component; 
    } 

    public virtual void MethodA() 
    { 
     _component.MethodA(); 

     // Do something else 
    } 

    public virtual void MethodB() 
    { 
     _component.MethodB(); 
    } 
} 

我可以通過嘲笑包裹IComponent的孤立現在測試ExtendedComponent。

這種方法的缺點是,如果我想向IComponent添加新方法,那麼我必須將新方法添加到Component和ExtendedComponent以及任何其他可能有很多實現的實現中。使用繼承我可以只添加新的方法到基礎組件,它不會破壞別的東西。

我真的希望能夠進行乾淨的測試,所以我偏愛構圖路線,但我擔心能夠進行單元測試並不是總是使用構圖而不是繼承的合理原因。在基本級別添加功能還需要創建許多乏味的委託方法。

我會很感激的人是如何接洽其他此類問題的一些建議

+2

我想,你誤解了一點'孤立'測試的概念。這意味着,您測試或組件不依賴於外部組件。但是,在繼承的情況下,基類不在您的範圍之外。它實際上是你的一部分。 – undefined

回答

2

使用組成你的做法是在所有practicallity大多數編譯器如何實現繼承,所以你一無所獲,但付出了沉重的代價(很多樣板代碼)。所以當存在一個關係和組合時,堅持繼承關係(當然這些關係並不是黃金也不是唯一規則)

2

您不需要擔心測試擴展組件'孤立',因爲它不'依賴'組件,它是一個組件(至少它是你編碼的方式)。

您最初爲組件類編寫的所有測試仍然正常,並且還在擴展類中測試了所有未更改的行爲。所有你需要做的就是編寫新的測試來測試擴展組件中的附加功能。

public class ComponentTests{ 
    [Fact] 
    public void MethodADoesSomething(){ 
    Assert.xxx(new Component().MethodA());//Tests the component class 
    } 

    [Fact] 
    public void MethodBDoesSomething(){ 
    Assert.xxx(new Component().MethodB());//Tests the component class 
    } 
} 

public class ExtendedComponentTests{ 
    [Fact] 
    public void MethodADoesSomething(){ 
    Assert.xxx(new ExtendedComponent().MethodA());//Tests the extended component class 
    } 
} 

您可以從上面看到MethodA功能已針對組件和擴展組件進行了測試。雖然新功能僅針對ExtendedComponent進行了測試。

0

你是對的 - 在不適合的地方使用繼承而不是合適的組合。根據你在這裏提供的信息,目前還不清楚哪一個更好。在這種情況下問自己哪一個更合適。通過使用繼承,您可以獲得多態性虛擬化的方法。如果你使用組合,你可以有效地將你的「前端」邏輯與孤立的「後端」邏輯分開 - 這種方法更容易,因爲改變底層組件不會對代碼的其他部分產生連鎖反應繼承往往是。

總而言之,這不應該影響你如何測試你的代碼。有許多框架供測試可用,但這不應該影響您選擇的設計模式。

+0

您一定意味着它不會影響測試的能力。代碼將根據其編碼方式進行不同的測試。 –

2

這裏的關鍵思想是,也可以在單元測試端繼承。 我在這種情況下使用以下方法。我將有一個單元測試用例的並行繼承層次結構。例如

[TestClass] 
public abstract class IComponentTest 
{ 
    [TestMethod] 
    public void TestMethodA() 
    { 
     // Interface level expectations. 
    } 
    [TestMethod] 
    public void TestMethodB() 
    { 
     // Interface level expectations. 
    } 
} 

[TestClass] 
public class ComponentTest : IComponentTest 
{ 
    [TestMethod] 
    public void TestMethodACustom() 
    { 
     // Test Component specific test, not general test 
    } 
    [TestMethod]  
    public void TestMethodBCustom() 
    { 
     // Test Component specific test, not general test 
    } 
} 
[TestClass] 
public class ExtendedComponent : ComponentTest 
{ 
    public void TestMethodACustom2() 
    { 
     // Test Component specific test, not general test 
    } 
} 

每個抽象測試類或具體類都會處理它自己的期望值。從而可以擴展和維護。

+0

+1提及測試夾具繼承。它通常是一種優雅的單元測試子類的層次結構。然而根據我的經驗,它需要在您的測試類中有一個SUT屬性,並在每個測試類的相關級別實例化,而您在示例中未顯示該屬性。 (我想知道你的IComponentTest方法的主體是什麼,你選擇哪個IComponent的實現並且在那裏新建?) – guillaume31