- Published on
Data driven tests
- Authors
- Name
- David Jimenez
There is a classic problem called FizzBuzz. It asks us to iterate over the first one hundred numbers, checking each to see if it is divisible by three or five taking action as follows:
- If divisible by three, then print "Fizz".
- If divisible by five, print "Buzz".
- If divisible by both three and five, print "FizzBuzz".
- If divisible by neither three nor five, print an empty string.
Imagine that to solve this problem, we write a helper method that given an integer returns the appropriate string:
public string GetStringToPrint(int input)
{
if (input % 3 == 0 && input % 5 == 0)
{
return "FizzBuzz";
}
if (input % 3 == 0)
{
return "Fizz";
}
if (input % 5 == 0)
{
return "Buzz";
}
return string.Empty;
}
We know that writing some unit tests could increase our confidence in the correctness of our method. We also know that we can take a shortcut, and ask ChatGPT to do it for us. This is a sample of what ChatGPT-3.5 returns:
[TestMethod]
public void Test_InputNotDivisibleByThreeOrFive_ReturnsEmptyString()
{
// Arrange
int input = 1;
// Act
string result = fizzBuzz.GetStringToPrint(input);
// Assert
Assert.AreEqual(string.Empty, result);
}
[TestMethod]
public void Test_InputDivisibleByThree_ReturnsFizz()
{
// Arrange
int input = 9; // 9 is divisible by 3
// Act
string result = fizzBuzz.GetStringToPrint(input);
// Assert
Assert.AreEqual("Fizz", result);
}
[TestMethod]
public void Test_InputDivisibleByFive_ReturnsBuzz()
{
// Arrange
int input = 20; // 20 is divisible by 5
// Act
string result = fizzBuzz.GetStringToPrint(input);
// Assert
Assert.AreEqual("Buzz", result);
}
[TestMethod]
public void Test_InputDivisibleByThreeAndFive_ReturnsFizzBuzz()
{
// Arrange
int input = 15; // 15 is divisible by both 3 and 5
// Act
string result = fizzBuzz.GetStringToPrint(input);
// Assert
Assert.AreEqual("FizzBuzz", result);
}
ChatGPT generated good tests: they provide coverage for the four cases our method is supposed to cover; their names are also descriptive though a bit wordy.
But what if we wanted to test some other cases we were not certain about? For example, what if we wanted to check that "FizzBuzz" is returned when the input is zero?
We could copy and paste the last test, give it a new name, and change the input from 15 to 0.
Before doing that, however, we should note that all four tests follow the same pattern. The only two things that differentiate them are:
- The value of the input when arranging the test, and
- The expected value in the assert.
What if our tests could take those two things, the input and the expected value, as parameters? MSTest (Microsoft Testing Framework) allows for that using the DataRow
attribute:
[TestMethod]
[DataRow(1, "")]
[DataRow(9, "Fizz")]
[DataRow(20, "Buzz")]
[DataRow(15, "FizzBuzz")]
public void GetStringToPrint_Returns_Expected(int input, string expected)
{
// Act
var actual = fizzBuzz.GetStringToPrint(input);
// Assert
Assert.AreEqual(expected, actual);
}
Now adding that new test to check that the method returns "FizzBuzz" when the input is zero boils down to adding a single line:
[TestMethod]
[DataRow(1, "")]
[DataRow(9, "Fizz")]
[DataRow(20, "Buzz")]
[DataRow(15, "FizzBuzz")]
[DataRow(0, "FizzBuzz")]
public void GetStringToPrint_Returns_Expected(int input, string expected)
{
// Act
var actual = fizzBuzz.GetStringToPrint(input);
// Assert
Assert.AreEqual(expected, actual);
}