Using NUnit’s TestCase Attribute

In the last post, we setup an NUnit test project and ran it in Visual Studio. In this post we are going to expand on that code to add more unit tests using the NUnit TestCase attribute.

Looking back at the last post, we are testing a method that converts an enum value to a friendly string by splitting the enum name at capitals. In that post, we only tested with one enum value. In this post, we will test with multiple enum values to ensure that the method handles every type of input. We will then remove code duplication by using the [TestCase] attribute to run one test many times with different data.

As always, the source code for this tutorial is on GitHub.

Testing Edge Cases

When testing a method, it is important that we test as many variations of input as possible. In the last post, we only tested that an enum with multiple words could be converted to a sentence – we tested that the enum value UnitTesting would convert to the string “Unit Testing”, but we should also test that it works with a single word, multiple words starting with a lowercase letter and invalid values.

To do this, one approach is to add tests for each scenario like this,

[Test]
public void CanConvertEnumIntoMultipleWords()
{
    // Arrange/Act
    var actual = TestTypes.UnitTesting.ToFriendlyString();

    // Assert
    Assert.That(actual, Is.Not.Null.And.EqualTo("Unit Testing"));
}

[Test]
public void CanConvertEnumIntoMultipleWordsWithFirstWordStartingWithLowerCase()
{
    // Arrange/Act
    var actual = TestTypes.integrationTesting.ToFriendlyString();

    // Assert
    Assert.That(actual, Is.Not.Null.And.EqualTo("Integration Testing"));
}

[Test]
public void CanConvertEnumSingleWord()
{
    // Arrange/Act
    var actual = TestTypes.Testing.ToFriendlyString();

    // Assert
    Assert.That(actual, Is.Not.Null.And.EqualTo("Testing"));
}

[Test]
public void CanConvertEnumSingleLowercaseWord()
{
    // Arrange/Act
    var actual = TestTypes.none.ToFriendlyString();

    // Assert
    Assert.That(actual, Is.Not.Null.And.EqualTo("None"));
}

Adding these tests revealed a bug in the implementation of the method where it doesn’t work if the first word starts with a lowercase letter which required a change to the Regex in that method. It should also capitalize each word. This is why we need to test as many possibilities as possible.

Using the TestCase Attribute

Each of our test methods has nearly identical code, we convert an enum to a string value, then we check if the string is correct. If we could pass the enum and the expected string into the function, then we could reduce the tests down to one method.

For situations like this, NUnit has the [TestCase] attribute. You can apply the [TestCase] attribute multiple times to a method and it will create a new test for every instance passing the parameters from the TestCase attribute into the test method. Using this, we can reduce the four tests to one that looks like this,

[TestCase(TestTypes.none, "None")]
[TestCase(TestTypes.Testing, "Testing")]
[TestCase(TestTypes.UnitTesting, "Unit Testing")]
[TestCase(TestTypes.integrationTesting, "Integration Testing")]
public void CanConvertEnumIntoFriendlyString(TestTypes value, string expected)
{
    // Arrange/Act
    var actual = value.ToFriendlyString();

    // Assert
    Assert.That(actual, Is.Not.Null.And.EqualTo(expected));
}

This simplifies our code and makes it easy to add new test cases. For example, if we want to see if our method handles single character enums, we could just add two new TestCases’s

[TestCase(TestTypes.a, "A")]
[TestCase(TestTypes.B, "B")]

We could also test for invalid values,

[TestCase((TestTypes)10, "")]

Other Parameters for TestCase

ExpectedResult

One of the more popular properties on the TestCase attribute is ExpectedResult. When this is used, the return value of the test method is checked for equality with the ExpectedResult. This can reduce code because you do not have to assert equality, it is done automatically.

Converting the method that tests the parsing of the enums would reduce it to this,

[TestCase((TestTypes)10, ExpectedResult = "")]
[TestCase(TestTypes.a, ExpectedResult = "A")]
[TestCase(TestTypes.B, ExpectedResult = "B")]
[TestCase(TestTypes.none, ExpectedResult = "None")]
[TestCase(TestTypes.Testing, ExpectedResult = "Testing")]
[TestCase(TestTypes.UnitTesting, ExpectedResult = "Unit Testing")]
[TestCase(TestTypes.integrationTesting, ExpectedResult = "Integration Testing")]
public string CanConvertEnumIntoFriendlyString(TestTypes value)
{
    return value.ToFriendlyString();
}

Common Parameters

The [TestCase] attribute supports many other parameters that can modify your tests or add information to them. Some of the more common are,

  • Author identifies the author of the test
  • Category provides a comma-delimited list of categories for this test
  • Description sets the description property of the test
  • TestName sets a test name instead of using the default generated name
  • TestOf specifies the Type that this test is testing
  • Explicit when set to true, the test can only be run explicitly. Use Reason to explain why
  • Ignore causes the test to not be run and specifies the reason

There are a few other parameters that you can use, see the [TestCase] attribute documentation for more information.