2

Closed

Consider allowing [PropertyData] to be found on derived classes of abstract test classes

description

This works fine....

public interface IFoo
{
int AddsNumbers(int x, int y);
}

public class Foo : IFoo
{
public int AddsNumbers(int x, int y) { return x + y; }
}

public abstract class IFooTest<T>
where T: IFoo
{
private Func<T> factory;

public IFooTest(Func<T> factory)
{
    this.factory = factory;
}

public static IEnumerable<object[]> Expectations
{
    get
    {
        yield return new object[] { 1, 2, 3 };
    }
}

[Theory]
[PropertyData("Expectations")]
public void Adds_ReturnsExpectedValues(int x, int y, int expected)
{
    Assert.Equal(expected, factory().AddsNumbers(x, y));
}
}

Consider where IFoo was really IFoo<T> and the signature of AddNumbers becomes T AddsNumbers(T x, T y) -- trivial example, but sufficient for illustrative purposes I think. In this case, I'd like to see the abstract test class provides the reusable test code meat, and derived classes provide the data for the tests So, I would like to see something like this work:

public abstract class IFooTest<T>
where T: IFoo
{
protected Func<T> factory;

public IFooTest(Func<T> factory)
{
    this.factory = factory;
}

[Theory]
[PropertyData("Expectations")]
public void Adds_ReturnsExpectedValues(int x, int y, int expected)
{
    Assert.Equal(expected, factory().AddsNumbers(x, y));
}
}

public class FooTest : IFooTest<Foo>
{
public FooTest()
    : base(() => new Foo())
{ }

public static IEnumerable<object[]> Expectations
{
    get
    {
        yield return new object[] { 1, 2, 3 };
    }
}
}

As it stands, this yields

Adds_ReturnsExpectedValues has failed

System.InvalidOperationException : An exception was thrown while getting data for theory xUnit.Theory.Test.FooTest.Adds_ReturnsExpectedValues:
System.ArgumentException: Could not find public static property Expectations on xUnit.Theory.Test.IFooTest`1[[xUnit.Theory.Test.Foo, xUnit.Theory.CodeRush.Bug, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
at Xunit.Extensions.PropertyDataAttribute.GetData(MethodInfo methodUnderTest, Type[] parameterTypes)
at Xunit.Extensions.TheoryAttribute.<GetData>d__7.MoveNext()
at Xunit.Extensions.TheoryAttribute.EnumerateTestCommands(IMethodInfo method)
Xunit.Extensions.TheoryAttribute.<>c__DisplayClass5.b__1()
Xunit.Extensions.TheoryAttribute.LambdaTestCommand.Execute(Object testClass)


I think this is a pretty straightforward tweak to the existing code base, but didn't know if you had any theoretical or design objections to enabling such functionality.
Closed May 5, 2013 at 2:27 AM by BradWilson
We have considered this and decided not to implement it.

The inverse feature ("find property data on base classes") is coming in v2.

comments

Itamar wrote Aug 3, 2012 at 9:31 AM

This is an easy fix, just bring the source for PropertyDataAttribute into your project and change this:
    public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest, Type[] parameterTypes)
    {
        Type type = PropertyType ?? methodUnderTest.DeclaringType;
        PropertyInfo propInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
Into this:
    public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest, Type[] parameterTypes)
    {
        Type type = PropertyType ?? methodUnderTest.ReflectedType;
        PropertyInfo propInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);