如何使用Autofixture在子属性中自动设置父对象

3

我想使用 autofixture 创建一个对象图,其中子对象有对父对象的引用。例如:

class A
{
  public List<B> Children { get; set; }
}

class B
{
  public A Parent { get; set; }
}

我尝试创建一个处理递归的行为,但我不知道如何将父对象作为属性的内容发出。

public class AutoParentOnRecursionBehavior : ISpecimenBuilderTransformation
{
    public ISpecimenBuilder Transform(ISpecimenBuilder builder)
    {
        if (builder == null)
            throw new ArgumentNullException("builder");

        return new RecursionGuard(builder, new AutoParentOnRecursionHandler());
    }
}

public class AutoParentOnRecursionHandler : IRecursionHandler
{
    public object HandleRecursiveRequest(
        object request,
        IEnumerable<object> recordedRequests)
    {
        object handleRecursiveRequest = recordedRequests.First(x => x.Equals(request));
        return ....
    }
}

谢谢。

编辑: 我正在考虑一种通用的方式,无需指定类型A和B甚至属性Children。 对于包含对象的属性类型的所有属性,将它们设置为父对象。换句话说,所有触发递归保护的类型的属性都应该设置为创建层次结构中的最后一个对象。


2
相关内容:https://dev59.com/0mQn5IYBdhLWcg3wIUBn - Nikos Baxevanis
2
你能否更改类的API呢?这里提出的设计不仅难以处理AutoFixture,而且通常会给您和您的团队成员带来持续的痛苦。 - Mark Seemann
1个回答

2
我假设以下内容:
  1. 如果您直接创建B,则B.Parent应为null
  2. 如果您创建A,则应将B.Parent设置为包含它的A实例。
使用PostProcessorFor时,可以通过以下简单的自定义来实现此目标:
fixture.Customize<B>(c => c.Without(x => x.Parent));
fixture.PostProcessorFor<A>(a => { foreach(var b in a.Children) b.Parent = a; });

以下列举一些断言来说明结果:

var b = fixture.Create<B>();
Assert.Null(b.Parent);

var a = fixture.Create<A>();
Assert.True(a.Children.All(b => ReferenceEquals(b.Parent, a)));

谢谢你的回答,我肯定可以使用它,但我想要一种更通用的方式,而不必指定类型A和B甚至属性Children。对于包含对象的属性类型的所有属性,将它们设置为父对象。换句话说,递归保护触发的类型的所有属性都将被设置为创建层次结构中的最后一个对象。 - hokkos
1
@hokkos:如果我要触发层级结构中倒数第二个类型的属性的递归保护,该怎么办呢?例如:A.Children(of type B) -> B.Children(of type C) -> c.GrandFather(of type A) - Daniel Hilgarth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接