I have just started using AutoFixture and have this semi-complex data structure that I would like to create some specimen for. In the tests I am working with I don\'t care
Creating A with an empty list of Bs
It's easy to create an instance of A with an empty list of Bs:
var fixture = new Fixture();
fixture.Inject(Enumerable.Empty<B>());
var a = fixture.Create<A>();
Creating a small tree
It's much more difficult to create a small tree, but it's possible. You're already on track with your thinking about RecursionGuard
. In order to verify if this could work, I copied most of the code from RecursionGuard
and created this DepthRecursionGuard
as a proof of concept:
public class DepthRecursionGuard : ISpecimenBuilderNode
{
private readonly ISpecimenBuilder builder;
private readonly Stack<object> monitoredRequests;
public DepthRecursionGuard(ISpecimenBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
this.monitoredRequests = new Stack<object>();
this.builder = builder;
}
public object Create(object request, ISpecimenContext context)
{
if (this.monitoredRequests.Count(request.Equals) > 1)
return this.HandleRecursiveRequest(request);
this.monitoredRequests.Push(request);
var specimen = this.builder.Create(request, context);
this.monitoredRequests.Pop();
return specimen;
}
private object HandleRecursiveRequest(object request)
{
if (typeof(IEnumerable<B>).Equals(request))
return Enumerable.Empty<B>();
throw new InvalidOperationException("boo hiss!");
}
public ISpecimenBuilderNode Compose(IEnumerable<ISpecimenBuilder> builders)
{
var builder = ComposeIfMultiple(builders);
return new DepthRecursionGuard(builder);
}
public virtual IEnumerator<ISpecimenBuilder> GetEnumerator()
{
yield return this.builder;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private static ISpecimenBuilder ComposeIfMultiple(
IEnumerable<ISpecimenBuilder> builders)
{
var isSingle = builders.Take(2).Count() == 1;
if (isSingle)
return builders.Single();
return new CompositeSpecimenBuilder(builders);
}
}
Notice the changed implementation of the Create
method, as well as the specific handling of IEnumerable<B>
in HandleRecursiveRequest
.
In order to make this usable from a Fixture
instance, I also added this DepthRecursionBehavior
:
public class DepthRecursionBehavior : ISpecimenBuilderTransformation
{
public ISpecimenBuilder Transform(ISpecimenBuilder builder)
{
return new DepthRecursionGuard(builder);
}
}
This enabled me to create a small tree:
var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList().ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthRecursionBehavior());
var a = fixture.Create<A>();
While this is possible, it's, in my opinion, too hard, so I've created a work item to make it easier in the future.
Update 2013.11.13: From AutoFixture 3.13.0, the recursion depth can be configured via that API.