NUnit TestCaseSource example (advanced)

Just a quick reminder on how to set up TestCaseSource with NUnit. It can save a few lines of code when you want to test the same method using different sets of values. We will use TDD approach to implement our sample method and unit test it.

Plan

Our plan is very straightforward:

  1. Write unit tests for a method first, so we defined our logic (TDD approach).
  2. Implement the method, so the tests would pass.
  3. Apply TestCaseSource to minimize amount of code used for testing.

Business logic – implement a method to sum two integers. Conditions:

  • If total is less or equals to zero, return zero.
  • If total is greater or equals 100, return 100.
  • Otherwise, return sum of both integers.
  • Do not need to implement input validation to check if input values are valid integers.

And… Action!

First of all, let’s define our logic by implementing several unit tests for the method we will be testing, and add empty calculator method just to make sure our code compiles, and tests are failing.

using NUnit.Framework;

namespace UnitTestProject1
{
    [TestFixture]
    public class UnitTest1
    {
        [Test]
        public void Sum_TwoIntegers_CorrectSumTotal()
        {
            // Arrange
            const int a = 10;
            const int b = 28;
            const int expectedResult = 38;
            var calc = new Calc();

            // Act
            var result = calc.Sum(a, b);

            // Assert
            Assert.AreEqual(expectedResult, result);
        }

        [Test]
        public void Sum_NegativeTotal_ReturnsZero()
        {
            // Arrange
            const int a = -10;
            const int b = -5;
            const int expectedResult = 0;
            var calc = new Calc();

            // Act
            var result = calc.Sum(a, b);

            // Assert
            Assert.AreEqual(expectedResult, result);
        }

        [Test]
        public void Sum_GreaterThan100Total_Returns100()
        {
        // Arrange
        const int a = 50;
        const int b = 51;
        const int expectedResult = 100;
        var calc = new Calc();

        // Act
        var result = calc.Sum(a, b);

        // Assert
        Assert.AreEqual(expectedResult, result);
        }
    }

    public class Calc
    {
        public int Sum(int a, int b)
        {
            throw new System.NotImplementedException();
        }
    }
}

Run unit tests — all of them will fail for an obvious reason :)

Now let’s implement the method to meet the business requirements.

public int Sum(int a, int b)
{
    var sum = a + b;

    if (sum > 100)
    {
        return 100;
    }

    return sum < 0 ? 0 : sum;
}

Run unit tests and all of them will pass this time. This means you implemented unit tests to cover the method’s logic. However, instead of having 3 different methods for unit testing, can slightly optimize our code and make it shorter and easier to read. Let’s move on.

TestCaseSource approach

Now let’s change unit tests to use a test case source to provide testing cases for the same method, this way we can leave a single test and just add different values as parameters. Modified code below. Please note I’ve also added two more test cases to check edge cases of sum total being equal to 0 and 100.

private static IEnumerable<TestCaseData> SumCalcTestData
{
    get
    {
        yield return new TestCaseData(10, 28, 38);
        yield return new TestCaseData(-10, -5, 0);
        yield return new TestCaseData(50, 51, 100);
        yield return new TestCaseData(-100, 100, 0);
        yield return new TestCaseData(-100, 200, 100);
    }
}

[Test, TestCaseSource(nameof(SumCalcTestData))]
public void Sum_TestCases(int a, int b, int expectedResult)
{
    // Arrange
    var calc = new Calc();

    // Act
    var result = calc.Sum(a, b);

    // Assert
    Assert.AreEqual(expectedResult, result);
}

If you run the test, it should pass as it did before. Well done!

For your reference, the full code snippet is below.

using System.Collections.Generic;
using NUnit.Framework;

namespace UnitTestProject1
{
    [TestFixture]
    public class UnitTest1
    {
        private static IEnumerable<TestCaseData> SumCalcTestData
        {
            get
            {
                yield return new TestCaseData(10, 28, 38);
                yield return new TestCaseData(-10, -5, 0);
                yield return new TestCaseData(50, 51, 100);
                yield return new TestCaseData(-100, 100, 0);
                yield return new TestCaseData(-100, 200, 100);
            }
        }

        [Test, TestCaseSource(nameof(SumCalcTestData))]
        public void Sum_TestCases(int a, int b, int expectedResult)
        {
            // Arrange
            var calc = new Calc();

            // Act
            var result = calc.Sum(a, b);

            // Assert
            Assert.AreEqual(expectedResult, result);
        }
    }

    public class Calc
    {
        public int Sum(int a, int b)
        {
            var sum = a + b;

            if (sum > 100)
            {
                return 100;
            }

            return sum < 0 ? 0 : sum;
        }
    }
}

Conclusions

While implementing separate unit tests per test case works, however sometimes using TestCaseSource approach can reduce code duplication and let you focus more on coding the test rather than copying and pasting the same test just to slightly modify it. Hope you find this useful!