Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xUnit.net Theory where MemberData is from derived class

Using xUnit.net, it is possible for a Theory to have its MemberData originate from a derived class?

public abstract class BaseTest
{
    public abstract IEnumerable<object[]> Data();

    [Theory]
    [MemberData(nameof(Data))]
    public void TestData(string expected, string actual)
    {
        // assert goes here
    }
}

public class ComplexTest : BaseTest
{
    public override IEnumerable<object[]> Data()
    {
        // data goes here
    }
}

The code above results in the following error message:

System.NotSupportedException : Specified method is not supported.

like image 270
budi Avatar asked May 24 '17 21:05

budi


4 Answers

Another way of doing this (and IMO cleaner), is to put your test scenarios in their own specific classes and just define each scenario set as a separate MemberData attribute:

public class BaseTest
{
    [Theory]
    [MemberData(nameof(TestScenarios1.Data), MemberType = typeof(TestScenarios1)]
    [MemberData(nameof(TestScenarios1.MoreData), MemberType = typeof(TestScenarios1)]
    [MemberData(nameof(TestScenarios2.DifferentData), MemberType = typeof(TestScenarios2)]
    public void TestData(string expected, string actual)
    {
        // assert goes here
    }
}

public class TestScenarios1
{
    public static IEnumerable<object[]> Data()
    {
        // data goes here
    }

    public static IEnumerable<object[]> MoreData()
    {
        // data goes here
    }
}

public class TestScenarios2
{
    public static IEnumerable<object[]> DifferentData()
    {
        // data goes here
    }
}
like image 69
Ayb4btu Avatar answered Nov 04 '22 10:11

Ayb4btu


As far as I know, this is not possible. MemberData's data is required to be static, therefore the data must originate from its own class.

public static IEnumerable<object[]> Data()
{
    // data goes here
}

[Theory]
[MemberData(nameof(Data))]
public void TestData(string expected, string actual)
{
    // assert goes here
}
like image 21
budi Avatar answered Nov 04 '22 09:11

budi


You're correct in your answer. Posting this non-answer in case it happens to spark an idea.

MemberData can be passed params, which might help depending on your specific scenario?

Other than that, the best you can prob do is to put a forwarder:

public abstract class BaseTest
{
    protected void RunTest(string expected, string actual)
    {
        Assert.Equal(expected, actual);
    }
}

public class ComplexTest : BaseTest
{
    static IEnumerable<object[]> Data() = 
    {
        { "a", "a" }
    }

    [Theory, MemberData(nameof(Data))]
    void TestData(expected, actual) => base.RunTest(expected, actual);
}
like image 2
Ruben Bartelink Avatar answered Nov 04 '22 10:11

Ruben Bartelink


Another way is to add MemberData only to derived class. It will check members by name and take the correct one from the current class. The only thing that analyzer with warn that you must specify member in base class (that by default treated as an error), so you have to disable this rule. If you try to specify this method in both classes - base and derived only base classes would be used. The issue about analyzer described in xunit github: https://github.com/xunit/xunit/issues/1243

Your example may look:

public abstract class BaseTest
{
    [Theory]
#pragma warning disable xUnit1015 // MemberData must reference an existing member
    [MemberData(nameof(Data))]
#pragma warning restore xUnit1015 // MemberData must reference an existing member
    public void TestData(string expected, string actual)
    {
        // assert goes here
    }
}

public class ComplexTest : BaseTest
{
    public static IEnumerable<object[]> Data()
    {
        return data;
        // data goes here
    }
}
like image 2
Noof Saeidh Avatar answered Nov 04 '22 10:11

Noof Saeidh