Search code examples
c#unit-testinginterfacenunit

Reduce duplication in NUnit tests for different implementations of interface


I have the following interface:

public interface IStack
{
    int Peek();
    void Push(int i);
}

And two implementations:

public class LinkedListStack : IStack
{
    public void Push(int x)
    {
     ...
    }

    public int Peek()
    {
     ...
    }
}

public class ArrayStack : IStack
{
    public void Push(int i)
    {
     ...
    }

    public int Peek()
    {
     ...
    }
}

For my unit tests - I currently have something like this, two files containing the same tests - the only difference being the implementation:

LinkedListStackTest.cs:

public class LinkedListStackTest
{
    [Test]
    public void PushToStack()
    {
        //Arrange
        IStack stack = new LinkedListStack();

        //Act
        stack.Push(1);

        //Assert
        Assert.AreEqual(1, stack.Peek());
    }
}

ArrayStackTest.cs

public class ArrayStackTest
{
    [Test]
    public void PushToStack()
    {
        //Arrange
        IStack stack = new ArrayStack();

        //Act
        stack.Push(1);

        //Assert
        Assert.AreEqual(1, stack.Peek());
    }
}

Given the tests for the implementations should be the same - is there a way I can write a single set of NUnit tests that will run against all my implementations of IStack?


Solution

  • You can use the TestCaseSourceAttribute:

    [Test]
    [TestCaseSource(typeof(StackTestCases))]
    public void PushToStack(IStack stack)
    {
        //Arrange/Act
        stack.Push(1);
    
        //Assert
        Assert.AreEqual(1, stack.Peek());
    }
    

    Implementation of StackTestCases:

    internal class StackTestCases : IEnumerable
    {
        public static IEnumerable TestCases
        {
            get
            {
                yield return new TestCaseData(new LinkedListStack());
                yield return new TestCaseData(new ArrayStack());
            }
        }
    
        /// <inheritdoc />
        public IEnumerator GetEnumerator()
        {
            return TestCases.GetEnumerator();
        }
    }
    

    Please note that the test method will take an IStack as a parameter:

    [Test]
    [TestCaseSource(typeof(StackTestCases))]
    public void PushToStack(IStack stack)
    

    ...And you can return the different implementations of IStack in TestCases property of StackTestCases:

    yield return new TestCaseData(new LinkedListStack());
    yield return new TestCaseData(new ArrayStack());