How to customize AutoFixture behavior for specific classes

I need to enable AutoFixture to instantiate type types with circular references (from an API provided by a third party). To do this, I can remove the default ThrowingRecursionBehavior, as shown below:

public class RecursiveObjectCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
            .ToList()
            .ForEach(b => fixture.Behaviors.Remove(b));
        fixture.Behaviors.Add(new OmitOnRecursionBehavior());
    }
 }

However, I understand that this will result in deletion ThrowingRecursionBehaviorfor all types when the setting is applied through an attribute. How to restrict altered behavior to only apply to specific types?

+2
source share
2 answers

You need to create custom behavior for it.

Here's how to get started:

public class OmitOnRecursionForRequestBehavior : ISpecimenBuilderTransformation
{
    private const int DefaultRecursionDepth = 1;
    private readonly int recursionDepth;
    private readonly object request;

    public OmitOnRecursionForRequestBehavior(object request)
        : this(request, DefaultRecursionDepth)
    {
    }

    public OmitOnRecursionForRequestBehavior(
        object request,
        int recursionDepth)
    {
        if (request == null)
            throw new ArgumentNullException("request");
        if (recursionDepth < 1)
            throw new ArgumentOutOfRangeException(
                "recursionDepth",
                "Recursion depth must be greater than 0.");

        this.recursionDepth = recursionDepth;
        this.request = request;
    }

    public ISpecimenBuilder Transform(ISpecimenBuilder builder)
    {
        if (builder == null)
            throw new ArgumentNullException("builder");

        return new RecursionGuard(
            builder,
            new RecursionForRequestHandler(
                request,
                new OmitOnRecursionHandler(),
                builder),
            recursionDepth);
    }
}

public class RecursionForRequestHandler : IRecursionHandler
{
    private readonly object request;
    private readonly IRecursionHandler handlerForRequest;
    private readonly ISpecimenBuilder handler;

    public RecursionForRequestHandler(
        object request,
        IRecursionHandler handlerForRequest,
        ISpecimenBuilder handler)
    {
        if (request == null)
            throw new ArgumentNullException("request");
        if (handlerForRequest == null)
            throw new ArgumentNullException("handlerForRequest");
        if (handler == null)
            throw new ArgumentNullException("handler");

        this.request = request;
        this.handlerForRequest = handlerForRequest;
        this.handler = handler;
    }

    public object HandleRecursiveRequest(
        object request,
        IEnumerable<object> recordedRequests)
    {
        if (this.request.Equals(request))
            return handlerForRequest.HandleRecursiveRequest(
                request,
                recordedRequests);

        return handler.Create(request, new SpecimenContext(handler));
    }
}

Here's how you use it:

fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(MyType)));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(AnotherType)));

, ThrowingRecursionBehavior, , StackOverflowException.

, recursionDepth 1, ThrowingRecursionBehavior recursionDepth.

public class DepthThrowingRecursionBehavior : ISpecimenBuilderTransformation
{
    private readonly int recursionDepth;

    public DepthThrowingRecursionBehavior(int recursionDepth)
    {
        if (recursionDepth < 1)
            throw new ArgumentOutOfRangeException(
                "recursionDepth",
                "Recursion depth must be greater than 0.");

        this.recursionDepth = recursionDepth;
    }

    public ISpecimenBuilder Transform(ISpecimenBuilder builder)
    {
        if (builder == null)
            throw new ArgumentNullException("builder");

        return new RecursionGuard(
            builder,
            new ThrowingRecursionHandler(),
            recursionDepth);
    }
}

fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
    .ToList()
    .ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthThrowingRecursionBehavior(2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(MyType), 2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(AnotherType), 1));
+3

Marcio Rinaldi, , 1.

internal class OmitOnRecursionBehavior<T> : ISpecimenBuilderTransformation
{
    public ISpecimenBuilder Transform(ISpecimenBuilder builder)
    {
        if (builder == null)
            throw new ArgumentNullException("builder");

        return new RecursionGuard(
            builder,
            new RecursionHandler<T>(
                new OmitOnRecursionHandler(),
                builder));
    }
}

internal class RecursionHandler<T> : IRecursionHandler
{
    private readonly IRecursionHandler handlerForRequest;
    private readonly ISpecimenBuilder builder;

    public RecursionHandler(
        IRecursionHandler handlerForRequest,
        ISpecimenBuilder builder)
    {
        if (handlerForRequest == null)
            throw new ArgumentNullException("handlerForRequest");
        if (builder == null)
            throw new ArgumentNullException("builder");

        this.handlerForRequest = handlerForRequest;
        this.builder = builder;
    }

    public object HandleRecursiveRequest(
        object request,
        IEnumerable<object> recordedRequests)
    {
        if (request.Equals(typeof(T)))
            return handlerForRequest.HandleRecursiveRequest(
                request,
                recordedRequests);

        return builder.Create(request, new SpecimenContext(builder));
    }
}

:

fixture.Behaviors.Add(new OmitOnRecursionBehavior<ClassWithCircularReference>());
+3

All Articles