Parameterise Your Tests: Use this One Simple Trick to Increase Test Coverage
Tags: csharp, dotnet, testing, tutorial
Published: 2023-05-31 13:54:00

Unit tests are important because they act as basic checks, ensuring that your app’s components are working correctly. To catch as many errors as possible, it’s often a good idea to run the logic while simulating different use cases. Last week we looked at how to write our first unit test. In our example, we wrote a single test. It checked that the result of squaring an input of `3` was `9`.

Admittedly, squaring a number isn’t the most complex of logic. However, it’s worth checking at least two scenarios, even if only to see that our method doesn’t return a hardcoded value of `9` and that we got lucky with the input.

So how do we check the results for squaring other numbers too?

Let’s explore two ways we can do this. For reference, code for our original (NUnit) test is shown below.

``` csharp
[Test]
public void MultiplicationServiceCanSquareNumber()
{
    // Arrange

    var service = new MultiplicationService();

    // Act

    var result = service.Square(3);

    // Assert

    Assert.That(result, Is.EqualTo(9));
}
```

## Duplicating Tests with Different Values

As the test is small, one option would be to duplicate the test with different values. We could then give each test a name reflecting the values we are testing:

``` csharp
[Test]
public void MultiplicationServiceReturns4WhenSquaring2()
{
    // Arrange

    var service = new MultiplicationService();

    // Act

    var result = service.Square(2);

    // Assert

    Assert.That(result, Is.EqualTo(4));
}

[Test]
public void MultiplicationServiceReturns9WhenSquaring3()
{
    // Arrange

    var service = new MultiplicationService();

    // Act

    var result = service.Square(3);

    // Assert

    Assert.That(result, Is.EqualTo(9));
}
```

This is sometimes the best thing to do – especially in more complex tests where the respective Arrange sections differ slightly.

## Parameterising the Tests

With the test we’re currently writing, we simply want to try different inputs with identical test setups. So instead, let’s add some parameter to the test. To do this, we need:

* To change the attribute that we decorate our test with.

* An input value we want to test our logic with.

* The expected output for the given input.

The following code shows what this might look like.

``` csharp
[TestCase(2, 4)]
[TestCase(3, 9)]
[TestCase(4, 16)]
public void MultiplicationServiceCanSquareNumber(int input, int expected)
{
    // Arrange

    var service = new MultiplicationService();

    // Act

    var result = service.Square(input);

    // Assert

    Assert.That(result, Is.EqualTo(expected));
}
```

When we run our tests, we’ll see something like Image 1. We can see that our three test cases are displayed as separate tests in the Visual Studio Test Explorer.


![The Visual Studio Test Explorer showing passing unit tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjik4pw0cmk4awhvd9hz.png)

<figcaption>Image 1: The Visual Studio Test Explorer showing our test results</figcaption>

We’ve been using NUnit for our sample code. If you’re using xUnit, it’s possible to do something similar by using the `Theory` and `InlineData` attributes.

## Other Use Cases

Here we’ve looked at the use case of verifying calculation results using different inputs and expected outputs. Other scenarios where using test cases could be useful include testing:

* A method’s string input is case insensitive. Here we could check for identical results when using different inputs, e.g.

  * EXAMPLE

  * Example

  * example

  * ExAmPle

* A method handles empty string values gracefully. Test cases that we might want to try include:

  * A string consisting of spaces only.

  * An empty string.

  * `null`

* [A custom numeric UI control respects its minimum value when decrementing values](https://github.com/Ant-f/WorkingFilesList/blob/main/Solution/WorkingFilesList.Core.Test/Controls/Command/DecrementNumericUpDownValueTests.cs#L82).

## Summary

To maximise your test coverage, it’s often a good idea to run a piece of logic with different inputs. You can then check whether the outcome for each input was as expected. Two ways that you can test logic with different input values are:

* Duplicating a test and modifying the name, setup, and assertion.

* Passing inputs and expected outputs as test parameters.

When simulating logic running in identical conditions but with different inputs, it might be best to use the second option. To do this in NUnit, simply change a test’s signature to include parameters and replace the `[Test]` attribute with `[TestCase]`.

You’ll be able to expand your test coverage for different use cases quickly and easily. And as you’ll avoid unnecessarily duplicating code, you’ll be keeping your test projects tidy too.

---

Thanks for reading!

This series aims to cover the basics of software testing, giving you more confidence in your code and how you work with it.

If you found this article useful, please consider signing up for my weekly [newsletter](https://webdeveloperdiary.substack.com/?sd=pf) for more articles like this delivered straight to your inbox (link goes to Substack).