LP13
LP13

Reputation: 34149

XUnit custom data attribute executes for every test

I have a custom data attribute in xUnit.

public class MyTest : DataAttribute
{
    private readonly int _x;
    private readonly int _y;

    public MyTest(int x, int y)
    {
        _x = x;
        _y = y;
    }

    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        return new[] { new object[] { _x, _y } };
    }
}

Then i have two tests

    [Theory]
    [MyTest(100, 200)]
    public void Test1(int x, int y)
    {
        var r = x * y;
        Assert.Equal(20000, r);
    }


    [Theory]
    [MyTest(300, 400)]
    public void Test2(int x, int y)
    {
        var r = x * y;
        Assert.Equal(120000, r);
    }

Then i put breakpoint in MyTest attribute's GetData() method. And then in Test Explorer i right clicked on Test1 and click debug selected tests.

When i do that i notice MyTest attribute get executed twice, for Test1 andTest2. I am not sure why its also getting executed for the Test2?

If i have more tests with MyTest attributes, then it executes for each of those tests. Is there any way to disable this behavior?

Upvotes: 1

Views: 2772

Answers (1)

bugged87
bugged87

Reputation: 3142

By default, xUnit enables enumeration during discovery of any custom data attribute that inherits directly from Xunit.Sdk.DataAttribute. You can make this enumeration configurable for your custom data attribute by adding the following to your implementation.

[DataDiscoverer("MyNamespace.MyTestDataAttribute+Discoverer", "MyAssemblyFullyQualifiedName")]
public class MyTestDataAttribute : DataAttribute
{
    protected class Discoverer : DataDiscoverer
    {
        public override bool SupportsDiscoveryEnumeration(IAttributeInfo dataAttribute, IMethodInfo testMethod)
        {
            return dataAttribute.GetNamedArgument<bool>(nameof(MyTestDataAttribute.SupportsDiscoveryEnumeration));
        }
    }

    private readonly int _x;
    private readonly int _y;

    /// <summary>
    ///     Returns <c>false</c> if the data attribute wants to skip enumerating data during discovery. This will cause the
    ///     theory to yield a single test case for all data, and the data discovery will be during test execution instead of
    ///     discovery.
    /// </summary>
    public bool SupportsDiscoveryEnumeration { get; set; }

    public MyTest(int x, int y)
    {
        _x = x;
        _y = y;
    }

    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        return new[] { new object[] { _x, _y } };
    }
}

This configures xUnit to skip data enumeration by default for your custom data attribute during the discovery phase, and instead delays that discovery until the execution phase.

Therefore, you would simply implement your tests as normal, and opt-in only those tests you wish to be discovered (for whatever reason) before test execution.

[Theory]
[MyTest(100, 200)]
public void Test1(int x, int y)
{
    int r = x * y;
    Assert.Equal(20000, r);
}

[Theory]
[MyTest(300, 400, SupportsDiscoveryEnumeration = true)]
public void Test2(int x, int y)
{
    int r = x * y;
    Assert.Equal(120000, r);
}

Upvotes: 2

Related Questions