zchpit zchpit - 3 months ago 62
C# Question

Pass complex parameters to [Theory]

Xunit have a nice feature, that you can create one test with 'theory' parameter, and put data in 'InlineData' parameter and xUnit will generete many tests, and test it all -> Link to example

I want to have something like this, but parameter to my method is not 'simple data' (like string, int, double), but list of my class:

public static void WriteReportsToMemoryStream(IEnumerable<MyCustomClass> listReport, MemoryStream ms, StreamWriter writer)
{
}

Answer

There are many xxxxData attributes in XUnit. Check out for example PropertyData attribute.

You can implement a property that returne IEnumerable<object[]>. Each of object[] that this method generates will be then "unpacked" as a parameters for a single call to your [Theory] method.

Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.

See i.e. these examples from here:

PropertyData Example

public class StringTests2
{
    [Theory, PropertyData(nameof(SplitCountData))]
    public void SplitCount(string input, int expectedCount)
    {
        var actualCount = input.Split(' ').Count();
        Assert.Equal(expectedCount, actualCount);
    }

    public static IEnumerable<object[]> SplitCountData
    {
        get
        {
            // Or this could read from a file. :)
            return new[]
            {
                new object[] { "xUnit", 1 },
                new object[] { "is fun", 2 },
                new object[] { "to test with", 3 }
            };
        }
    }
}

ClassData Example

public class StringTests3
{
    [Theory, ClassData(typeof(IndexOfData))]
    public void IndexOf(string input, char letter, int expected)
    {
        var actual = input.IndexOf(letter);
        Assert.Equal(expected, actual);
    }
}

public class IndexOfData : IEnumerable<object[]>
{
    private readonly List<object[]> _data = new List<object[]>
    {
        new object[] { "hello world", 'w', 6 },
        new object[] { "goodnight moon", 'w', -1 }
    };

    public IEnumerator<object[]> GetEnumerator()
    { return _data.GetEnumerator(); }

    IEnumerator IEnumerable.GetEnumerator()
    { return GetEnumerator(); }
}
Comments