Having an actual decimal value as parameter for an attribute (example xUnit.net's [InlineData]

2 min read 07-10-2024
Having an actual decimal value as parameter for an attribute (example xUnit.net's [InlineData]


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:

  1. [MemberData]: This attribute tells xUnit.net to get its test data from a separate method.
  2. DiscountData(): This static method returns an IEnumerable<object[]>, where each object array represents a test case.
  3. 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.