Passing Decimal Values to Your Tests: A Guide to [InlineData] in xUnit.net
The Problem:
You're writing unit tests in xUnit.net, and you need to feed specific decimal values into your methods. But [InlineData]
only accepts strings, leaving you with awkward conversions and potential errors.
Here's the Scenario:
Imagine you're testing a function that calculates discounts. You want to verify it works correctly for various decimal percentages:
public class DiscountCalculatorTests
{
[Theory]
[InlineData("0.10", 100, 10)] // awkward string for a decimal!
[InlineData("0.25", 200, 50)]
public void CalculateDiscount_ReturnsCorrectValue(string discountPercentage, decimal originalPrice, decimal expectedDiscount)
{
// Arrange
var calculator = new DiscountCalculator();
// Act
var actualDiscount = calculator.CalculateDiscount(decimal.Parse(discountPercentage), originalPrice);
// Assert
Assert.Equal(expectedDiscount, actualDiscount);
}
}
The Solution:
xUnit.net doesn't directly support decimals in [InlineData]
. However, there's a simple and elegant workaround: use data annotations to define your data.
public class DiscountCalculatorTests
{
[Theory]
[MemberData(nameof(DiscountData))]
public void CalculateDiscount_ReturnsCorrectValue(decimal discountPercentage, decimal originalPrice, decimal expectedDiscount)
{
// Arrange
var calculator = new DiscountCalculator();
// Act
var actualDiscount = calculator.CalculateDiscount(discountPercentage, originalPrice);
// Assert
Assert.Equal(expectedDiscount, actualDiscount);
}
public static IEnumerable<object[]> DiscountData()
{
yield return new object[] { 0.10m, 100m, 10m };
yield return new object[] { 0.25m, 200m, 50m };
}
}
Explanation:
[MemberData]
: This attribute tells xUnit.net to get its test data from a separate method.DiscountData()
: This static method returns anIEnumerable<object[]>
, where each object array represents a test case.- Direct Decimal Values: Notice how we now provide decimal values directly. No need for strings and parsing!
Benefits:
- Cleaner Code: Avoids string conversions and improves readability.
- Type Safety: The compiler will catch errors if you accidentally pass the wrong data type.
- Data Organization: Separates test data from the test method, making it easier to manage and modify.
Additional Tips:
- Reusable Data: You can easily reuse the
DiscountData()
method in other tests. - Complex Data: For more complex scenarios, consider using external data sources like CSV files or databases.
- Test Data Generation: For large data sets, use tools like AutoFixture to automatically generate test data.
Conclusion:
By leveraging [MemberData]
and defining separate data methods, you can effortlessly pass decimal values to your xUnit.net tests, making your code cleaner, more efficient, and less prone to errors.