As part of the development cycle, I worked on unit tests for a new module. To streamline the process, Usually I use NUnit’s TestCase annotation, which allows defining multiple input parameters and expected outcomes in one test method. This made it easy to cover a wide range of scenarios while keeping the tests concise and maintainable. Example below how this annotation is used for testing.
[TestCase(1, 2, 3)]
[TestCase(-1, -2, -3)]
[TestCase(0, 0, 0)]
public int CalculatorAdd(int a, int b, int expected)
{
// Arrange
var calculator = new Calculator();
// Act
var result = calculator.Add(a, b);
// Assert
Assert.That(result, Is.EqualTo(expected));
}However, my situation turned out to be a bit different, since I needed to supply a List of objects as input. Unfortunately, using the annotation as shown above led to the following error.
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
While troubleshooting the limitation with the [TestCase] annotation, I discovered another option in NUnit called [TestCaseSource]. This attribute offered the flexibility needed to shape input parameters as required by my test methods. With [TestCaseSource], I could easily pass types such as complex lists or custom groupings.
When testing a method like Calculate that expects an input for example of IGrouping<string, CustomObject>, [TestCaseSource] allows you to specify a static method that produces tailored test data for these cases.
[Test]
[TestCaseSource(nameof(GetCalculateTestCases))]
public void Calculate(TestGrouping testGrouping, double? expected)
{
Module.Calculate(testGrouping).Should().Be(expected);
}
private static IEnumerable<TestCaseData> GetCalculateTestCases()
{
yield return new TestCaseData(
new TestGrouping("ID1", new[]
{
new CustomObject { Value = 100 },
new CustomObject { Value = 150 }
}),
250
);
yield return new TestCaseData(
new TestGrouping("ID1", new[]
{
new CustomObject { Value = 100 },
new CustomObject { Value = 0 }
}),
100
);
yield return new TestCaseData(
new TestGrouping("ID1", new[]
{
new CustomObject { Value = 0 },
new CustomObject { Value = 0 }
}),
0
);
}
public class TestGrouping(string key, IEnumerable<CustomObject> items) : IGrouping<string, CustomObject>
{
public string Key { get; } = key;
public IEnumerator<CustomObject> ItemsEnumerator { get; } = items.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => items.GetEnumerator();
}The [Test] attribute is required when using the [TestCase] attribute, but when using [TestCaseSource], it’s optional, and you can omit the [Test] attribute if desired.
In the example above, the Should() assertion is available thanks to the Fluent Assertions library, which enhances test readability and expressiveness by providing a fluent syntax for checks.