I'm trying to create AutoPropertyDataAttribute
based on CompositeDataAttribute
from this example AutoFixture: PropertyData and heterogeneous parameters.
It works with single set of parameters, but fails with more sets of parameters. Here is code:
public static IEnumerable<object[]> NumericSequence
{
get
{
yield return new object[] {1};
//yield return new object[] {2};
}
}
[Theory]
[AutoPropertyData("NumericSequence")]
public void Test(int? p1, int? p2, int? p3)
{
Assert.NotNull(p1);
Assert.NotNull(p2);
}
public class AutoPropertyDataAttribute : CompositeDataAttribute
{
public AutoPropertyDataAttribute(string propertyName)
: base(
new DataAttribute[] {
new PropertyDataAttribute(propertyName),
new AutoDataAttribute()
})
{
}
}
Trying to uncomment the second yield
will break test with message:
System.InvalidOperationException: Expected 2 parameters, got 1 parameters
at Ploeh.AutoFixture.Xunit.CompositeDataAttribute.<GetData>d__0.MoveNext()
at Xunit.Extensions.TheoryAttribute.<GetData>d__7.MoveNext()
at Xunit.Extensions.TheoryAttribute.EnumerateTestCommands(IMethodInfo method)
Same happens with ClassDataAttribute
I ran into this issue and decided to implement a custom DataAttribute to solve the problem. I couldn't use either attribute as a base class (reasons below) so I just took the things I needed from the source of each. Thank you OSS :)
Things to note:
PropertyDataAttribute
as a base classAutoDataAttribute
as a base classGist
Or inline source below:
What actually happens
The
NumericSequence
[PropertyData]
defines two iterations.The composition of
NumericSequence
[PropertyData]
with[AutoData]
assumes that there is enough data on each iteration.However, the actual composition is:
That's why in the 2nd iteration you eventually run out of data.
Composition
The
CompositeDataAttribute
respects the LSP in a sense that it is programmed against the base of all data theories, theDataAttribute
class.(That is, there is no assumption that all attributes are composed with
[AutoData]
at the end.)For that reason, it can't simply jump from the 2nd iteration to the 1st iteration and grab some
[AutoData]
values – that would break the LSP.What you could do
Make the actual composition look like:
By defining two properties:
And then, the original test can be written as:
The test executes twice and
n1
is always supplied by[PropertyData]
whilen2
andn3
are always supplied by[AutoData]
.As a workaround you can restructure the
AutoPropertyDataAttribute
a bit and use theCompositeDataAttribute
internally, rather than deriving from it. Derive from thePropertyDataAttribute
instead:Then override the
GetData
method to loop over the values returned by thePropertyDataAttribute
, and leverage AutoFixture'sInlineAutoData
(which derives fromCompositeDataAttribute
) to fill in the rest of the parameters:The outer loop iterates over the values returned by the
PropertyData
(each iteration is a row, with some of the cells filled in). The inner loop fills in the remaining cells.It's not the prettiest thing, but it seems to work. I like Mark's idea to have AutoFixture try to fill in the remaining cells. One less piece of glue code to write :)
Hope this helps,
Jeff.