Search code examples
c#.netunit-testingxunit

xUnit - How to pass (string, string) to InlineData


Title is pretty self explanatory. How do I pass (string, string) to inline data?

using System.Security.Cryptography;
using Xunit;

namespace Summer_Outfit.UnitTesting;

public class OutfitTests
{
    [Fact]
    public void Execute_DegreesTimeFromDay_ReturnsOutfitShoes()
    {
        // Arrange
        const int degrees = 20;
        const string timeFromDay = "Morning";
        var outfit = new Outfit();
        var expected = ("Shirt", "Moccasins");

        // Act
        var actual = outfit.Execute(degrees, timeFromDay);

        // Assert
        Assert.Equal(expected, actual);
    }
    
    [Theory]
    [InlineData(20, "Morning", ("Shirt", "Moccasins"))] // compile-time error here
    [InlineData(20, "Afternoon", ("Shirt", "Moccasins"))] // compile-time error here
    [InlineData(20, "Evening", ("Shirt", "Moccasins"))] // compile-time error here
    public void Execute_MultipleDegreesTimeFromDay_ReturnsOutfitShoes(int degrees, string timeFromDay, (string, string)expected)
    {
        // Arrange
        var outfit = new Outfit();

        // Act
        var actual = outfit.Execute(degrees, timeFromDay);

        // Assert
        Assert.Equal(expected, actual);
    }
}

Solution

  • According to the language specification section given by @JoshuaRobinson in the comment:

    21.2.4 Attribute parameter types The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:

    One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.

    The type object.

    The type System.Type.

    Enum types.

    Single-dimensional arrays of the above types.

    A constructor argument or public field that does not have one of these types, shall not be used as a positional or named parameter in an attribute specification.

    So if you didn't want to make them separate parameters in your method, as a different workaround you could use an array:

    [InlineData(20, "Morning", new string[] {"Shirt", "Moccasins"})]
    public void Execute_MultipleDegreesTimeFromDay_ReturnsOutfitShoes(int degrees, string timeFromDay, string[] expected)
    
    

    You'd still have to make a tuple out of it for the comparison, however:

    Assert.Equal((expected[0], expected[1]), actual);
    

    If you want to keep it as a tuple, you can use a MemberData or ClassData data source. See https://hamidmosalla.com/2017/02/25/xunit-theory-working-with-inlinedata-memberdata-classdata/ for examples.