I am writing a series of collection classes in C#, each of which implement similar custom interfaces. Is it possible to write a single collection of unit tests for an interface, and automatically run them all on several different implementations? I would like to avoid any duplicated testing code for each implementation.
I'm willing to look into any framework (NUnit, etc.) or Visual Studio extension to accomplish this.
For those looking to do the same, I posted my concrete solution, based off of avandeursen's accepted solution, as an answer.
Yes, that is possible. The trick is to let your unit class test hierarchy follow the class hierarchy of your code.
Let's assume you have an interface Itf
with implementing classes C1
and C2
.
You first create a test class for Itf
(ItfTest
). To actually exercise the test, you need to create a mock implementation of your Itf
interface.
All tests in this ItfTest
should pass on any implementation of Itf
(!). If not, your implementation does not conform to the Liskov Substitution Principle (the "L" in Martin's SOLID principles of OO design)
Thus, to create a test case for C1
, your C1Test
class can extend ItfTest
. Your extension should replace the mock object creation with the creation of a C1
object (injecting it in, or using a GoF factory method). In this way, all ItfTest
cases are applied to instances of type C1
. Furthermore, your C1Test
class can contain additional test cases specific to C1
.
Likewise for C2
. And you can repeat the trick for deeper nested classes and interfaces.
References: Binder's Polymorphic Server Test pattern, and McGregor's PACT -- Parallel Architecture for Component Testing.
Expanding on Joe's answer, you can use the [TestCaseSource] attribute in NUnit in a similar way to MBUnit's RowTest. You could create a test case source with your class names in there. You could then decorate every test which the TestCaseSource attribute. Then using Activator.CreateInstance you could cast to the interface and you'd be set.
Something like this (note - head compiled)
string[] MyClassNameList = { "Class1", "Class2" };
[TestCaseSource(MyClassNameList)]
public void Test1(string className)
{
var instance = Activator.CreateInstance(Type.FromName(className)) as IMyInterface;
...
}
This is my concrete implementation based off of avandeursen's answer:
[TestClass]
public abstract class IMyInterfaceTests
{
protected abstract IMyInterface CreateInstance();
[TestMethod]
public void SomeTest()
{
IMyInterface instance = CreateInstance();
// Run the test
}
}
Each interface implementation then defines the following test class:
[TestClass]
public class MyImplementationTests : IMyInterfaceTests
{
protected override IMyInterface CreateInstance()
{
return new MyImplementation();
}
}
SomeTest
is run once for each concrete TestClass
derived from IMyInterfaceTests
. By using an abstract base class, I avoid the need for any mock implementations. Be sure to add TestClassAttribute
to both classes or this won't work. Lastly, you can add any implementation-specific tests (such as constructors) to the child class if desired.
You can use the [RowTest] attributes in MBUnit to do this. The example below shows where you pass the method a string to indicate which interface implementation class you want to instantiate, and then creates this class via reflection:
[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:
}
In the [Row] attributes, you can pass any arbitrary number of input parameters, such as input values for testing or expected values to be returned by method invocations. You will need to add arguments of the corresponding types as test method input arguments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With