我正在用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