Creating recursive tree with AutoFixture

后端 未结 1 1473
伪装坚强ぢ
伪装坚强ぢ 2020-12-01 16:49

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

相关标签:
1条回答
  • 2020-12-01 17:23

    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.

    0 讨论(0)
提交回复
热议问题