我正在用C#編寫一系列集合類,每個集合類都實現類似的自定義接口。是否可以爲接口編寫單個單元測試集合,並自動在幾個不同的實現上運行它們?我想避免每個實現的任何重複的測試代碼。我可以實現一系列可重用測試來測試接口的實現嗎?
我願意查看任何框架(NUnit等)或Visual Studio擴展來完成此操作。
對於那些希望做同樣的,我貼我的具體的解決方案,基於斷avandeursen's accepted solution,爲an answer。
我正在用C#編寫一系列集合類,每個集合類都實現類似的自定義接口。是否可以爲接口編寫單個單元測試集合,並自動在幾個不同的實現上運行它們?我想避免每個實現的任何重複的測試代碼。我可以實現一系列可重用測試來測試接口的實現嗎?
我願意查看任何框架(NUnit等)或Visual Studio擴展來完成此操作。
對於那些希望做同樣的,我貼我的具體的解決方案,基於斷avandeursen's accepted solution,爲an answer。
是的,那是可能的。訣竅是讓您的單元類測試層次結構遵循代碼的類層次結構。
我們假設您有一個接口Itf
,其中實現類C1
和C2
。
您首先要爲Itf
(ItfTest
)創建一個測試類。要實際執行測試,您需要創建Itf
接口的模擬實現。
此ItfTest
中的所有測試應該通過Itf
(!)的任何實施。如果沒有,您的實現不符合
因此Liskov Substitution Principle(在馬丁的SOLID原則OO設計的「L」),來創建一個測試用例C1
,您C1Test
類可以擴展ItfTest
。您的擴展應該用創建C1
對象(將其注入或使用GoF factory method)替換模擬對象創建。通過這種方式,所有ItfTest
個案都適用於C1
類型的實例。此外,您的C1Test
類可以包含特定於C1
的其他測試用例。
同樣對於C2
。你可以重複使用更深的嵌套類和接口。
參考文獻:Binder's Polymorphic Server Test pattern, and McGregor's PACT - 組件測試的並行體系結構。
爲了避免模擬實現,我只是將我的'ItfTest'類聲明爲抽象,並聲明瞭一個'protected abstract Itf CreateInstance();'函數存根。 (請注意'ItfTest'和'C1Test'都需要'[TestClass]'屬性。) – dlras2 2012-02-20 22:37:28
是的,我也使用了工廠方法,因爲它更簡單。我在JUnit中使用了這種測試方法,其中沒有'[TestClass]'屬性,但超類中的'@ Test'註釋被繼承,導致超類測試用例在子類中重新運行。並且:感謝編輯。 – avandeursen 2012-02-21 06:47:40
您可以使用MBUnit中的[RowTest]屬性來執行此操作。下面,其中傳遞的方法的字符串,以指示要實例,然後該接口的實現類示出了例如經由反射創建此類:
[RowTest]
[Row("Class1")]
[Row("Class2")]
[Row("Class3")]
public void TestMethod(string type)
{
IMyInterface foo = Activator.CreateInstance(Type.GetType(type)) as IMyInterface;
//Do tests on foo:
}
在[行]屬性,可以通過任意數量的的輸入參數,例如測試的輸入值或方法調用返回的預期值。您將需要添加相應類型的參數作爲測試方法輸入參數。
在Joe's答案上展開,您可以使用類似MBUnit的RowTest的方式在NUnit中使用[TestCaseSource]屬性。你可以在那裏創建一個帶有你的類名的測試用例源代碼。然後,您可以修飾TestCaseSource屬性的每個測試。然後使用Activator.CreateInstance你可以轉換到界面,你會被設置。
像這樣的東西(注 - 頭編譯)
string[] MyClassNameList = { "Class1", "Class2" };
[TestCaseSource(MyClassNameList)]
public void Test1(string className)
{
var instance = Activator.CreateInstance(Type.FromName(className)) as IMyInterface;
...
}
這是根據我的具體實施關閉的avandeursen's answer:
[TestClass]
public abstract class IMyInterfaceTests
{
protected abstract IMyInterface CreateInstance();
[TestMethod]
public void SomeTest()
{
IMyInterface instance = CreateInstance();
// Run the test
}
}
每個接口的實現則定義了以下測試類:
[TestClass]
public class MyImplementationTests : IMyInterfaceTests
{
protected override IMyInterface CreateInstance()
{
return new MyImplementation();
}
}
SomeTest
運行一次每個具體TestClass
來自IMyInterfaceTests
。通過使用抽象基類,我可以避免使用任何模擬實現。一定要將TestClassAttribute
添加到這兩個班,否則這將無法工作。最後,如果需要,您可以將任何特定於實現的測試(如構造函數)添加到子類。
添加了[lsp](http://stackoverflow.com/questions/tagged/lsp)作爲標記,因爲問題和答案適用於遵循LSP的任何類層次結構。 – avandeursen 2012-02-21 06:52:45