2
Vote

Default Greedy constructor behavior breaks filling

description

Hello!

I have a base class for all domain entities called EntityBase, with private GUID id field. If I explicitly define greedy constructor for each entity like that:

Fixture.Customize<User>(c => c.FromFactory( new ConstructorInvoker(new GreedyConstructorQuery() )));

AutoFixture fills every field and ID.. I wanted to make greedy behavior deafult by:

Fixture.Customizations.Add(new ConstructorInvoker( new GreedyConstructorQuery()));

now AutoFixture fills ID properly but all other fields are just null.

Am I missing something ?

thanks!

And also, base class customizations are ignored, is this by design:

Fixture.Customize<EntityBase>(c => c.FromFactory( new ConstructorInvoker(new GreedyConstructorQuery() )));

comments

ploeh wrote Jun 26, 2011 at 12:23 PM

Sorry about the long delay in replying.

No, you're not missing anything. Customizations don't have AutoProperties since in most cases that would be quite surprising - particularly if you define a Customization that disables AutoProperties. This means that if you want AutoProperties, you must enable it explicitly.

At that point, we're definitely moving into arcane territory. What you want to do is certainly possibly, but I admit that it could be more intuitive. Instead of just adding the ConstructorInvoker directly, it must be wrapped in a PostProcessor. Here's how to do it:

fixture.Customizations.Add(new Postprocessor(
new ConstructorInvoker(new GreedyConstructorQuery()),
new AutoPropertiesCommand().Execute,
new AnyTypeSpecification()));
I'll leave this work item open, as I'll consider whether to define a more intuitive API for this.

Regarding your other question: This is being tracked by [workitem:1747]

gberberoglu wrote Jun 27, 2011 at 12:39 PM

thanks for the reply, it's working great now.

I have two minor questions just to understand Autofixture beter, I'll appreciate if you can answer anytime.
  1. I noticed that ordering of the following Customize and Customizations.Add statements are important, i.e. if we call .Customize AFTER .Customizations.Add, they don't work.. I'm not sure if this is supposed to be..
Fixture.Customize(new ObjectHydratorCustomization());
Fixture.Customize(new MultipleCustomization());

Fixture.Customizations.Add(new Postprocessor(new ConstructorInvoker(new GreedyConstructorQuery()), new AutoPropertiesCommand().Execute, new AnyTypeSpecification()));
  1. How can you specify ConstructorInvoker in a "post" processor ? Postprocessor seems like sth executed after class generated.
thanks for the great lib!

ploeh wrote Jun 29, 2011 at 10:23 PM

  1. Can you elaborate on what doesn't work? The Customizations added with the Customize method, or the stuff added to Customizations using the Add method?
As a general note, the order matters very much. That part is by design. Basically, the Customizations collection contains a lot of potential rules for creating objects, and the first rule which matches a request will be used, with all subsequent rules being ignored.

When you call IList.Add, the item is inserted after the last item, which means that the last Customization you add also has the lowest precedence. I hope that perhaps the documentation can be helpful, but if not, I'd appreciate any feedback you might have: http://autofixture.codeplex.com/wikipage?title=Fixture%20Architecture&referringTitle=Architecture

However, it's worth noting that if you use the Customize<T> fluent API, the last Customize<T> method call overwrites the previous customizations for the same type. I found that to be the most natural model in isolation, but I admit that it might not be the most intuitive behavior in combination with the other ways to customize a Fixture...
  1. The Postprocessor class is simply a Decorator that performs post-processing on the returned result from the decorated ISpecimenBuilder.