24

I have a data type with multiple constructors and I need AutoFixture to choose the greediest (one with the most parameters). The default behaviour is to choose the constructor with the smallest number.

The author's blog post, http://blog.ploeh.dk/2009/03/24/HowAutoFixtureCreatesObjects.aspx doesn't seem to imply there's a way of overriding this behaviour, so is it possible, and if so, how?

RichK
  • 11,318
  • 6
  • 35
  • 49

1 Answers1

31

This is certainly possible.

To change the strategy for a single type (MyClass):

fixture.Customize<MyClass>(c => c.FromFactory(
    new MethodInvoker(
        new GreedyConstructorQuery())));

To change the strategy across the board:

fixture.Customizations.Add(
    new MethodInvoker(
        new GreedyConstructorQuery()));

As it turns out, however, using GreedyConstructorQuery across the board is most likely problematic, as the following code snippet demonstrates. Imagine a class with this constructor:

public Foo(string name)
{
    this.name = name;
}

This test will throw an exception:

[Test]
public void GreedyConstructor()
{
    Fixture fixture = new Fixture();
    fixture.Customizations.Add(new MethodInvoker(new GreedyConstructorQuery()));

    Foo foo = fixture.CreateAnonymous<Foo>();
}

The exception thrown is:

Ploeh.AutoFixture.ObjectCreationException : AutoFixture was unable to create an instance from System.SByte*, most likely because it has no public constructor, is an abstract or non-public type.

So what's that about the SByte*? There's no SByte* in Foo...

Well, yes there is. By placing the MethodInvoker in Customization, it overrides all default creation strategies, including the one for strings. Instead, it goes looking for the greediest constructor for string and that is:

public String(sbyte* value, int startIndex, int length, Encoding enc);

And there's the sbyte*...


It's still possible to replace the modest constructor selection algorithm with a greedy algorithm, it's just a tad more involved than I first realized.

What you can do is this:

Write a small class like this one:

public class GreedyEngineParts : DefaultEngineParts
{
    public override IEnumerator<ISpecimenBuilder> GetEnumerator()
    {
        var iter = base.GetEnumerator();
        while (iter.MoveNext())
        {
            if (iter.Current is MethodInvoker)
                yield return new MethodInvoker(
                    new CompositeMethodQuery(
                        new GreedyConstructorQuery(),
                        new FactoryMethodQuery()));
            else
                yield return iter.Current;
        }
    }
}

and create the Fixture instance like this:

Fixture fixture = new Fixture(new GreedyEngineParts());

That should work.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Nice, I like that you can specify the behaviour for a particular type. Thanks – RichK Sep 14 '12 at 19:25
  • AutoFixture is actually throwing an ObjectCreationException when I use your second example, with the message "AutoFixture was unable to create an instance from System.SByte*..." My data type is very simple and certainly doesn't depend on a byte*! Is this a bug or have I misused the framework? – RichK Sep 19 '12 at 11:02
  • I've just tried, and everything works fine using your first example – RichK Sep 19 '12 at 11:04
  • It's not a (known) bug. Can you provide a repro? – Mark Seemann Sep 19 '12 at 11:54
  • I've sent an email to the address on your blog with a sample solution demoing the behavior. Thank for looking into it. – RichK Sep 19 '12 at 13:41
  • Thanks very much for all the follow up work you've provided to this question, Mark. – RichK Sep 22 '12 at 22:07
  • 3
    @MarkSeemann, should GreedyEngineParts be used most of the time instead of new MethodInvoker( new GreedyConstructorQuery). If yes, please update your blog http://blog.ploeh.dk/2011/04/19/ConstructorstrategiesforAutoFixture/ and add the class to AutoFixture – Michael Freidgeim Jun 18 '13 at 21:02