【问题标题】:How do I customize AutoFixture behaviours for specific classes如何为特定类自定义 AutoFixture 行为
【发布时间】:2015-08-06 12:31:03
【问题描述】:

我需要启用 AutoFixture 以创建具有循环引用的类型实例(来自第三方提供的 API)。为此,我可以删除默认的ThrowingRecursionBehavior,如下所示:

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());
    }
 }

但是,我知道当通过属性应用自定义时,这将删除所有类型的 ThrowingRecursionBehavior。如何将修改后的行为限制为仅适用于特定类型?

【问题讨论】:

    标签: unit-testing autofixture


    【解决方案1】:

    您必须为此创建自定义行为。

    下面是开始:

    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));
        }
    }
    

    这就是你将如何使用它:

    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));
    

    【讨论】:

      【解决方案2】:

      使用 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>());
      

      【讨论】:

        猜你喜欢
        • 2021-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-07
        相关资源
        最近更新 更多