Testing every implementer of an interface with the same tests using NUnit

You have an interface and multiple implementers of that interface. You want to test all those implementing classes using the same tests. And you do not want to repeat yourself. That would be … like … Gah!

Using a unit test framework like NUnit that is a breeze.

You write the tests once. Using a reference to the interface in all of them. And then you tell your test runner to run these tests for every class that implements it. This way you test all these classes against the contract of the interface. Contract testing, pure and simple.

Let’s take another stab at dovetail joints:

    interface IJointer
    {
        IDoveTailJoint MakeDoveTailJoint(IPieceOfWood woodTails, IPieceOfWood woodPins);
    }

    interface IDoveTailJoint {}

    interface IPieceOfWood {}

Any implementer of the IJointer interface needs to honor the following contract for the MakeDoveTailJoint method:

  • Should throw ArgumentNullException when woodTails is unassigned
  • Should set ParamName of ArgumentNullException to woodTails when woodTails is unassigned
  • Should throw ArgumentNullException when woodPins is unassigned
  • Should set ParamName of ArgumentNullException to woodPins when woodPins is unassigned
  • Should return non-null reference to a DoveTailJoint when no exceptions are thrown
  • Should return DoveTailJoint with at least 1 tail
  • Should return DoveTailJoint with non-zero TailWidth
  • Should return DoveTailJoint with non-zero TailLength
  • Should return DoveTailJoint with non-zero FullPinWidth
  • Should return DoveTailJoint with non-zero SidePinWidth
  • Should return DoveTailJoint that fits woodTails’ width: ((number of tails) * TailWidth) + (((number of tails) – 1) * FullPinWidth) + (2 * SidePinWidth)

Once you know how to do it for one of these contract requirements, you can easily extend this to the others, so for brevity’s sake let’s focus on “Should return non-null reference to a DoveTailJoint when no exceptions are thrown”. The method to check this behavior would be something like:

    class PieceOfWood_Fake : IPieceOfWood {}

    public void MakeDoveTailJoint_No_Exceptions_Raised_Should_Return_Non_Null_DoveTailJoint()
    {
        var jointer = MakeJointer();
        var DoveTailJoint = jointer.MakeDoveTailJoint(new PieceOfWood_Fake(), 
            new PieceOfWood_Fake());
        Assert.IsNotNull(DoveTailJoint);
    }

So far so good. Now, you need to implement the MakeJointer method in such a way that when you run this method for the FirstJointer class, it returns an instance of FirstJointer and when you run it for the SecondJointer class, it returns an instance of SecondJointer.

Using NUnit, you can very simply do this by declaring a generic test class and constraining it so it can only be used for (types deriving from) IJointer:

    class IJointer_Contract<T> where T : IJointer, new()
    {
        IJointer MakeJointer() 
        {
            return new T();
        }

        public void MakeDoveTailJoint_No_Exceptions_Raised_Should_Return_Non_Null_DoveTailJoint()
        { // ... snip ... }
    }

The new() constraint on the class declaration enables you to create new instances of T. Without it, the compiler complains with the error message “Cannot create an instance of the variable type ‘T’ because it does not have the new() constraint” on the return new T(); line.

Now that you have a generic test class that can create a reference to IJointer, you still need to tell NUnit to run all the test methods in this class for every class implementing the IJointer interface.

With NUnit, that is as easy as adding an attribute to the test class:

    [TestFixture(typeof(FirstJointer))]
    [TestFixture(typeof(SecondJointer))]
    class IJointer_Contract<T> where T : IJointer, new()
    {
        // ... snip ...
    }

NUnit’s TestFixture attribute makes NUnit run the tests in the IJointer_Contract<T> test class with T substituted by the class specified in the attribute. In the above example, NUnit will run all the tests in IJointer_Contract<T> for both the FirstJointer and the SecondJointer classes.

IJointer_Tests_NUnit
Yeah, I know, the test fails… The Jointer classes still only satisfy the compiler by having return null; as their implementation for the MakeDoveTailJoint method. The other tests succeed only because the tests themselves are not yet implemented. See my moan about this in How to test a class hierarchy without repeating yourself.

That’s it. Enjoy!

What code had you wondering how to test it? I’d love hearing from you by email or in the comments below. I read everything and will try to help where and as best I can.



Leave a Reply

Your email address will not be published. Required fields are marked *

*

Show Buttons
Hide Buttons