2015-08-06 4 views
2

Мне нужно включить AutoFixture для создания экземпляров типов с круговыми ссылками (из API, предоставляемых третьей стороной). Для этого можно удалить по умолчанию ThrowingRecursionBehavior, как показано ниже:Как настроить поведение AutoFixture для определенных классов

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 для всех типов, когда настройка применяется с помощью атрибута. Как я могу ограничить измененное поведение только применимым к конкретным типам?

ответ

3

Для этого вам нужно создать для себя обычное поведение.

Вот что-то начать:

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

Использование ответа от Марсио Ринальди Я создал простое решение для случая по умолчанию, где глубина рекурсии 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>());