2008-09-02 63 views
28

如果我有接口IFoo,並且有幾個類實現它,那麼根據接口測試所有這些類的最好/最優雅/最聰明的方法是什麼?NUnit - 如何測試實現特定接口的所有類

我想減少測試代碼的重複,但仍然'保持對'單元測試的原則。

您認爲最佳做法是什麼?我使用NUnit,但我想任何單元測試框架中的示例都是有效的

回答

13

如果你有類實現任何一個接口,那麼他們都需要實現該接口中的方法。爲了測試這些類,你需要爲每個類創建一個單元測試類。

讓我們改用更智能的路線;如果您的目標是避免代碼和測試代碼重複您可能想創建一個抽象類,而不是處理重複代碼

E.g.您有以下接口:

public interface IFoo { 

    public void CommonCode(); 

    public void SpecificCode(); 

} 

您可能要創建一個抽象類:

public abstract class AbstractFoo : IFoo { 

    public void CommonCode() { 
      SpecificCode(); 
    } 

    public abstract void SpecificCode(); 

} 

測試,很容易;實現抽象類的測試類無論是作爲一個內部類:

[TextFixture] 
public void TestClass { 

    private class TestFoo : AbstractFoo { 
     boolean hasCalledSpecificCode = false; 
     public void SpecificCode() { 
      hasCalledSpecificCode = true; 
     } 
    } 

    [Test] 
    public void testCommonCallsSpecificCode() { 
     TestFoo fooFighter = new TestFoo(); 
     fooFighter.CommonCode(); 
     Assert.That(fooFighter.hasCalledSpecificCode, Is.True()); 
    } 
} 

...還是讓測試類擴展抽象類本身是否適合你的想象。

[TestFixture] 
public void TestClass : AbstractFoo { 

    boolean hasCalledSpecificCode; 
    public void specificCode() { 
     hasCalledSpecificCode = true; 
    } 

    [Test] 
    public void testCommonCallsSpecificCode() { 
     AbstractFoo fooFighter = this; 
     hasCalledSpecificCode = false; 
     fooFighter.CommonCode(); 
     Assert.That(fooFighter.hasCalledSpecificCode, Is.True()); 
    }   

} 

擁有抽象類來處理接口隱含的通用代碼會給出更簡潔的代碼設計。

我希望這對你有意義。


作爲一個方面說明,這就是所謂的Template Method pattern常見的設計模式。在上面的例子中,模板方法是CommonCode方法,而SpecificCode被稱爲存根或掛鉤。這個想法是任何人都可以擴展行爲,而不需要知道幕後的東西。

很多框架依賴於這種行爲模式,例如, ASP.NET您必須在頁面或用戶控件中實現掛鉤,例如由Load事件調用的生成的Page_Load方法,模板方法會在幕後調用掛鉤。這裏有更多的例子。基本上,你必須實現的任何使用「load」,「init」或「render」這些詞語的模板方法都會被調用。

+1

@Spoike:+1,但是一個小問題是您的代碼不是C#。 – user7116 2008-10-06 17:48:35

0

我不使用NUnit,但我測試了C++接口。我會首先測試一個TestFoo類,它是它的一個基本實現,以確保通用的東西有效。那麼你只需要測試每個接口獨有的東西。

3

我不認爲這是最佳實踐。

簡單的事實是,一個接口只不過是一個方法實現的契約。這是一個而不是合同)a)該方法應該如何實現,b)該方法應該做什麼(它只能保證返回類型),我收集的兩個原因將是你想要的動機這種測試。

如果你真的想在你的方法實現的控制,你的選擇:

  • 實現它作爲一個抽象類的方法,並從繼承。你仍然需要將它繼承到一個具體的類中,但是你確定除非明確地重寫那個方法纔會做正確的事情。
  • 在.NET 3.5/C#3.0,實施該方法作爲擴展方法引用到Interface

實施例:

public static ReturnType MethodName (this IMyinterface myImplementation, SomeObject someParameter) 
{ 
    //method body goes here 
} 

任何實施適當引用該擴展方法將精確地發射該擴展方法,所以你只需要測試一次。

11

Jon Limjap不同意時,他說,

這不是在任一個。)該方法如何應實施和b。)什麼方法應該做的正是(它只保證合同返回類型),我收集的兩個原因是你想要這種測試的動機。

在返回類型中可能有很多未指定的合同部分。語言無關的例子:上鍊表,ArrayList中,CircularlyLinkedList

public interface List { 

    // adds o and returns the list 
    public List add(Object o); 

    // removed the first occurrence of o and returns the list 
    public List remove(Object o); 

} 

你的單元測試,和所有其它的應該不僅測試該列表自身的返回,而且他們已經適當修改。

關於按合同設計有earlier question,這可以幫助您指導干預這些測試的正確方向。

如果你不想合同的開銷,我建議試驗檯,沿着線什麼建議Spoike

abstract class BaseListTest { 

    abstract public List newListInstance(); 

    public void testAddToList() { 
    // do some adding tests 
    } 

    public void testRemoveFromList() { 
    // do some removing tests 
    } 

} 

class ArrayListTest < BaseListTest { 
    List newListInstance() { new ArrayList(); } 

    public void arrayListSpecificTest1() { 
    // test something about ArrayLists beyond the List requirements 
    } 
} 
1

當測試一個接口或基類的合同,我寧願讓測試框架自動地關心查找所有的實現者。這使您可以專注於測試的接口,併合理確信所有實現都將進行測試,而無需執行大量手動實現。

  • 對於xUnit.net,我創建一個Type Resolver庫來搜索特定類型的所有實施方案中(在xUnit.net擴展是隻是一個瘦包裝在類型解析器功能,所以也可以適用於其他框架使用)。
  • MbUnit中,可以在參數上使用CombinatorialTestUsingImplementations屬性。
  • 對於其他框架,提到的基類模式Spoike可能很有用。

除了測試界面的基礎知識之外,還應測試每個單獨的實現是否遵循其特定要求。

1

@Emperor XLII

我喜歡在MbUnit的組合測試的聲音,我試着NUnit的抽象基類,接口測試技術,雖然它的工作,你需要有一個單獨的測試一個類實現的每個接口的夾具(因爲在C#中沒有多重繼承 - 儘管可以使用內部類,這非常酷)。 實際上這很好,甚至可能是有利的,因爲它通過接口將實現類的測試分組。但是如果框架更聰明,那將會很好。如果我可以使用一個屬性來將一個類標記爲接口的「官方」測試類,並且該框架將搜索測試的程序集中所有實現該接口的類,然後在其上運行這些測試。

這會很酷。

1

[TestFixture]類的層次結構如何?將常用測試代碼放在基本測試類中,並將其繼承到子測試類中。

相關問題