Customizing customized types

Mar 28, 2010 at 6:51 AM

I hope that there is some way to accomplish this with the current tools.  It's a bit late, so I'm hopeful that I'm just overlooking the obvious!

I'm trying to apply a "template" ruleset to a class, and then later apply local customizations to that class.  Here, for instance, is my customization of an EntityObject, in which I exclude a navigation property that AutoFixtures is unable to set:

Fixtures.Customize<Raw.File>(ob => ob
        .Without(f => f.EntityKey)
        .Without(f => f.EntityState)
        .Without(f => f.FileAccessStat)
        .Without(f => f.FileAccessStatReference));

In my tests on this object, I'd like to maintain those rules and adjust certain other properties based on the specifics of the test.  Something like:

var file = Fixtures.Build<Raw.File>()
                   .With(f => f.Deleted, true)
                   .CreateAnonymous();

I guess this doesn't work because Build() resets the customization of the object.  Is there some way to customize the construction without overriding my initial rules?

Thank you very much for any help!

Coordinator
Mar 29, 2010 at 8:46 PM

AutoFixture doesn't have any explicit support for this kind of scenario where you want a base customization that you can keep building upon (see below).

The Build<T> method doesn't reset the customization of the object, but it ignores it. The difference is subtle, but had it reset it, a subsequent call to CreateAnonymous<T> would not use the customization, and it does. However, Build<T> ignores any customization already present because the essense of the Build method is that it's a one-off customization. In other words, the behavior of the Build<T> method ignores the customization by design.

However, here is what you can do: the Customize<T> method takes a single Func<LatentObjectBuilder<T>, ObjectBuilder<T>> as input, so you can explicitly remember the the value and use it to build upon.

Instead of doing this:

fixture.Customize<Foo>(ob => ob
    .Without(f => f.Bar)
    .Without(f => f.Baz));

You can do this:

Func<LatentObjectBuilder<Foo>, ObjectBuilder<Foo>> builderTransform = ob => ob
    .Without(f => f.Bar)
    .Without(f => f.Baz);
fixture.Customize(builderTransform);

A normal call to CreateAnonymous<Foo> will return a Foo instance without the Bar and Baz properties assigned, but you can use the builderTransform as a basis for further customizations:

fixture.Customize<Foo>(ob => builderTransform(ob)
    .With(f => f.Baz)
    .Without(f => f.Ploeh));

With this extended customization, a subsequent call to CreateAnonymous<Foo> will now return a Foo instance without the Bar and Ploeh properties, but with the Baz property.

If you'd like, you can expand this approach to remember customization Funcs for all types in a dictionary.

On a closing note, I'd like to ask why you want to do this? However, from your question, I think I can venture a guess: you are using the Entity Framework and need to switch off a lot of the auto-generated properties because many of them have circular references. When you have an externally defined API like this, there's really not a lot else you can do. However, if you do have control over the API in question, I would suggest that you rethink the design.

Since AutoFixture is a heuristic, convention-based engine, it has to follow the 80-20 rule to some extent. For a properly designed API, customizations of customizations shouldn't be necessary, so we currently don't have any specific plans to support it. However, I ask about your scenario out of genuine interest, because if it turns out that there's a reasonable scenario for this, we may have to rethink our strategy on this topic.

In any case, although we have no explicit plans to support customizations of customizations even in AutoFixture 2.0, the next version will sport a much more extensible engine. I haven't thought this completely through, but I think with that engine it would be possible to write a heuristic add-in for, say, EF that disables AutoProperties for all the problematic properties defined by EF (such as EntityKey, EntityState and all the circular referential navigation properties).

HTH

Mar 31, 2010 at 6:31 AM

Thank you so much for the in-depth reply!  My problem is indeed centered on EF and my inability to (easily) modify the auto-generated content.  The solution you've discussed seems quite sufficient to me - I suppose for a sustained testing strategy I'll want to look into a snazzy dictionary setup.

In the meantime, for what it's worth, I ended up writing a little class to initialize my entities with AF, and then go back and reassign the values that needed re-customization.  It actually turned out to be sort of handy because I could tack on a SaveChanges() call pretty cleanly when necessary.  Here's what it looks like in the test:

var vault = EFFixtures.Using(context).Create<Raw.Vault>()
                      .With(v => v.Deleted = true).AndSave();

The Create() calls the AF factory.

As for other (non-EF) opportunities to use this kind of functionality, I can only imagine one off hand that might at some point affect us.  We have various validation rules on a number of objects (using the DataAnnotations library), and I suppose it's possible that AF's pseudo-random content might bump up against those rules in the right sort of test.  With a property that looks like

[EmailAddress]
public string EmailAddress { get; set; }

that object won't be making it past the validator.  So I could imagine us solving that problem with a Do() specification to ensure that those objects always validate in addition to whatever testing customizations might need to be done on the object (eg. (Deleted = true), or whatever).

Just to be clear, I'm not requesting any features here..!  Your workaround would certainly work in this (imaginary) case as well, so it bothers me even less than it already did.  Thanks again for your thoughts, and thanks also for this excellent tool!

Coordinator
Apr 2, 2010 at 10:29 AM

Thank you for elaborating. I will keep an eye out for DataAnnotations, but typically attributes tend to depend on particular run-time environments, which means that they also tend to be inactive during unit testing (unless explicitly invoked/executed). FWIW, you may be interested in this related blog post about constrained input.

One of the points of AutoFixture 2.0's kernel architecture is to enable much more granular data creation scenarios, so that you would be able to register an add-in that explicitly intercepts and resolves requests for PropertyInfos where the type is a string and the property name includes the word EmailAddress. That is still in the future, though :)