我需要启用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
。如何将修改后的行为限制为仅适用于特定类型?
答案 0 :(得分:4)
您必须为此创建自定义行为。
这是开始的事情:
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));
答案 1 :(得分:4)
使用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>());