Parameterise Your Tests: Use this One Simple Trick to Increase Test Coverage
Hi Friends.
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.
[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:
[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.
[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.
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.
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.