36

For example, there is a interface IMyInterface, and three classes support this interface:

class A : IMyInterface
{
}

class B : IMyInterface
{
}

class C : IMyInterface
{
}

In the simplest way, I could write three test class : ATest, BTest, CTest and test them separately. However, since they support the same interface, most test code would be the same, it's hard to maintain. How can I use a simple and easy way to test a interface that is supported by different class?

(previously asked on the MSDN forums)

Shog9
  • 156,901
  • 35
  • 231
  • 235
Chetan
  • 46,743
  • 31
  • 106
  • 145

5 Answers5

49

If you want to run the same tests against different implementers of your interface using NUnit as an example:

public interface IMyInterface {}
class A : IMyInterface { }
class B : IMyInterface { }
class C : IMyInterface { }

public abstract class BaseTest
{
    protected abstract IMyInterface CreateInstance();

    [Test]
    public void Test1()
    {
        IMyInterface instance = CreateInstance();
        //Do some testing on the instance...
    }

    //And some more tests.
}

[TestFixture]
public class ClassATests : BaseTest
{
    protected override IMyInterface CreateInstance()
    {
        return new A();
    }

    [Test]
    public void TestCaseJustForA()
    {
        IMyInterface instance = CreateInstance();   
        //Do some testing on the instance...
    }

}

[TestFixture]
public class ClassBTests : BaseTest
{
    protected override IMyInterface CreateInstance()
    {
        return new B();
    }
}

[TestFixture]
public class ClassCTests : BaseTest
{
    protected override IMyInterface CreateInstance()
    {
        return new C();
    }
}
Paul
  • 571
  • 3
  • 17
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • 3
    +1 Nice answer! Nowadays NUnit supports generic test classes and the TestFixture attribute can be used to supply the specific types to be used when the test is run. I wrote a [blog post](http://softwareonastring.com/2015/03/22/testing-every-implementer-of-an-interface-with-the-same-tests-using-nunit) about how to test every implementer of an interface showcasing these features. – Marjan Venema Mar 28 '15 at 18:46
29

To test an interface with common tests regardless of implementation, you can use an abstract test case, and then create concrete instances of the test case for each implementation of the interface.

The abstract (base) test case performs the implementation-neutral tests (i.e. verify the interface contract) while the concrete tests take care of instantiating the object to test, and perform any implementation-specific tests.

mdma
  • 56,943
  • 12
  • 94
  • 128
  • 1
    I am new to unit testing and I took your advice a few days ago, however at least for PHPUnit I am finding it pretty difficult - there is not a way to really write an abstract test method as the abstract class cannot reference the extended class – Scott Anderson Apr 06 '20 at 15:47
3

Could create methods that take a parameter of type IMyInterface and have the actual test methods just call those methods passing in different concrete classes.

Davy8
  • 30,868
  • 25
  • 115
  • 173
3

You do not test the interface directly, but you may write an abstract class that tests the contract a particular implementation should extend. A test of a concrete class would then extend the abstract class

Kathy Van Stone
  • 25,531
  • 3
  • 32
  • 40
0

If you're using NUnit, then you could use Grensesnitt:

public interface ICanAdd {
    int Add(int i, int j); //dont ask me why you want different adders
}

public class winefoo : ICanAdd {
    public int Add(int i, int j)
    {
        return i + j;
    }
}

interface winebar : ICanAdd {
    void FooBar() ; 
}

public class Adder1 : winebar {
    public int Add(int i, int j) {
        return i + j;
    } 
    public void FooBar() {}
}

public class Adder2 : ICanAdd {
    public int Add(int i, int j) {
        return (i + 12) + (j - 12 ); //yeeeeeaaaah
    } 
}

[InterfaceSpecification]
public class WithOtherPlugins : AppliesToAll<ICanAdd>
{ 
    [TestCase(1, 2, 3)] 
    [TestCase(-1, 2, 1)]
    [TestCase(0, 0, 0)]
    public void CanAddOrSomething(int x, int y, int r)
    {
        Assert.AreEqual(subject.Add(x, y), r);
    }

    [TestCase(1, 2, Result = 3)]
    [TestCase(-1, 2, Result = 1)]
    [TestCase(0, 0, Result = 0)]
    public int CannAddOrSomethingWithReturn(int x, int y) {
        return subject.Add(x, y);
    }
}
James
  • 1,380
  • 11
  • 11