我正在用 C# 编写一系列集合类,每个集合类都实现了类似的自定义接口。是否可以为一个接口编写一个单元测试集合,并在几个不同的实现上自动运行它们?我想避免每个实现的任何重复测试代码。
我愿意研究任何框架(NUnit 等)或 Visual Studio 扩展来完成此任务。
对于那些希望做同样事情的人,我发布了基于avandeursen 接受的解决方案的具体解决方案作为答案。
我正在用 C# 编写一系列集合类,每个集合类都实现了类似的自定义接口。是否可以为一个接口编写一个单元测试集合,并在几个不同的实现上自动运行它们?我想避免每个实现的任何重复测试代码。
我愿意研究任何框架(NUnit 等)或 Visual Studio 扩展来完成此任务。
对于那些希望做同样事情的人,我发布了基于avandeursen 接受的解决方案的具体解决方案作为答案。
是的,这是可能的。诀窍是让您的单元类测试层次结构遵循代码的类层次结构。
假设您有一个Itf
带有实现类的接口,C1
并且C2
.
Itf
您首先为( ItfTest
)创建一个测试类。要实际进行测试,您需要创建Itf
接口的模拟实现。
这里的所有测试都ItfTest
应该通过Itf
(!) 的任何实现。如果不是,您的实现不符合Liskov 替换原则(Martin 的面向对象设计的SOLID原则中 的“L” )
因此,要为 创建测试用例C1
,您的C1Test
类可以扩展ItfTest
. 您的扩展应该用创建C1
对象(将其注入或使用GoF 工厂方法)替换模拟对象的创建。通过这种方式,所有ItfTest
情况都适用于 type 的实例C1
。此外,您的C1Test
课程可以包含特定于C1
.
同样对于C2
. 您可以对更深的嵌套类和接口重复该技巧。
您可以使用 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:
}
在 [Row] 属性中,您可以传递任意数量的输入参数,例如用于测试的输入值或方法调用返回的预期值。您将需要添加相应类型的参数作为测试方法输入参数。
扩展Joe 的答案,您可以使用 NUnit 中的 [TestCaseSource] 属性,其方式与 MBUnit 的 RowTest 类似。您可以在其中创建一个包含您的类名的测试用例源。然后,您可以装饰 TestCaseSource 属性的每个测试。然后使用 Activator.CreateInstance 你可以转换到界面并且你会被设置。
像这样的东西(注意 - 头部编译)
string[] MyClassNameList = { "Class1", "Class2" };
[TestCaseSource(MyClassNameList)]
public void Test1(string className)
{
var instance = Activator.CreateInstance(Type.FromName(className)) as IMyInterface;
...
}
这是我基于avandeursen 的回答的具体实现:
[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
到这两个类,否则这将不起作用。最后,如果需要,您可以将任何特定于实现的测试(例如构造函数)添加到子类。