abstract-class - AutoFixture 可以省略类层次结构之间的递归吗?

标签 abstract-class autofixture

考虑以下方式来代表家庭成员:

public abstract class Human { }
public abstract class Child : Human
{
    public ICollection<Parent> Parents { get; set; }
}
public abstract class Parent : Human
{
    public ICollection<Child> Children { get; set; }
}
public class Son : Child { }
public class Daughter : Child { }
public class Mum : Parent { }
public class Dad : Parent { }

现在,我要 AutoFixture生成 Parent ,在 Mum 之间随机选择和 Dad ,以及在 Son 之间随机选择 child 的地方和 Daughter .我也希望它省略递归,所以如果它来自 Parent并生成 Child , 可以省略回 Parent 的链接.

我尝试了以下自定义:
var fixture = new Fixture();

fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
    .ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());

var random = new Random();
fixture.Register<Parent>(() =>
{
    switch (random.Next(1, 2))
    {
        case 1:
            return fixture.Create<Mum>();
        case 2:
            return fixture.Create<Dad>();
        default:
            throw new NotImplementedException();
    }
});
fixture.Register<Child>(() =>
{
    switch (random.Next(1, 2))
    {
        case 1:
            return fixture.Create<Son>();
        case 2:
            return fixture.Create<Daughter>();
        default:
            throw new NotImplementedException();
    }
});

fixture.Create<Parent>();

但它抛出一个 InvalidCastException (见下文)。

有没有办法配置 AutoFixture 以便它考虑 Parent -> Child -> Parent递归,即使它实际上为每个实例随机选择了一个合适的子类?
Unhandled Exception: AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to
create an instance from AutoFixtureAbstractTrees.Parent because creation unexpectedly
failed with exception. Please refer to the inner exception to investigate the root cause of
the failure.

Request path:
        AutoFixtureAbstractTrees.Mum
          System.Collections.Generic.ICollection`1[AutoFixtureAbstractTrees.Child] Children
            System.Collections.Generic.ICollection`1[AutoFixtureAbstractTrees.Child]
              System.Collections.Generic.List`1[AutoFixtureAbstractTrees.Child]
                System.Collections.Generic.IEnumerable`1[AutoFixtureAbstractTrees.Child] collection
                  System.Collections.Generic.IEnumerable`1[AutoFixtureAbstractTrees.Child]
                    AutoFixtureAbstractTrees.Child
                      AutoFixtureAbstractTrees.Son
                        System.Collections.Generic.ICollection`1[AutoFixtureAbstractTrees.Parent] Parents
                          System.Collections.Generic.ICollection`1[AutoFixtureAbstractTrees.Parent]
                            System.Collections.Generic.List`1[AutoFixtureAbstractTrees.Parent]
                              System.Collections.Generic.IEnumerable`1[AutoFixtureAbstractTrees.Parent] collection
                                System.Collections.Generic.IEnumerable`1[AutoFixtureAbstractTrees.Parent]
                                  AutoFixtureAbstractTrees.Parent

Inner exception messages:
        System.InvalidCastException: Unable to cast object of type
        'AutoFixture.Kernel.OmitSpecimen' to type 'AutoFixtureAbstractTrees.Mum'.

最佳答案

您遇到此问题的原因是 AutoFixture 中的设计缺陷。当您使用 Create扩展方法,您本质上启动了一个新的解析上下文,而递归保护机制不会捕捉到它。

看起来,在这种情况下,您可以使用 ISpecimenBuilder 解决该问题。 s 和 Resolve来自 context而不是使用 Create扩展方法:

[Fact]
public void WorkAround()
{
    var fixture = new Fixture();

    fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
        .ForEach(b => fixture.Behaviors.Remove(b));
    fixture.Behaviors.Add(new OmitOnRecursionBehavior(3));

    var random = new Random();
    fixture.Customizations.Add(new ParentBuilder(random));
    fixture.Customizations.Add(new ChildBuilder(random));

    var actual = fixture.Create<Parent>();

    Assert.True(0 < actual.Children.Count);
}

此测试通过,并使用自定义类 ParentBuilderChildBuilder :
public class ParentBuilder : ISpecimenBuilder
{
    private readonly Random random;

    public ParentBuilder(Random random)
    {
        this.random = random;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;
        if (t == null || t != typeof(Parent))
            return new NoSpecimen();

        if (this.random.Next(0, 2) == 0)
            return context.Resolve(typeof(Mum));
        else
            return context.Resolve(typeof(Dad));
    }
}

public class ChildBuilder : ISpecimenBuilder
{
    private readonly Random random;

    public ChildBuilder(Random random)
    {
        this.random = random;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;
        if (t == null || t != typeof(Child))
            return new NoSpecimen();

        if (this.random.Next(0, 2) == 0)
            return context.Resolve(typeof(Son));
        else
            return context.Resolve(typeof(Daughter));
    }
}

综上所述,如 you've previously discovered ,您正在插入 AutoFixture 的极限。它并不是真正设计用于处理像这里显示的那样的复杂递归对象设计。

关于abstract-class - AutoFixture 可以省略类层次结构之间的递归吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48438939/

相关文章:

c# - 使用部分类而不是抽象类有什么好处?

AutoFixture:你能解冻数据类型吗?

C++标准方式创建 "Abstract Class"(纯虚类)

c++ - 将派生自基类的类的实例传递给函数

c# - 是否有一种简单的方法来创建 AutoFixture 自定义以限制枚举值?

c# - 如何简化单元测试 DDD Value objects Equality with AutoFixture

c# - 如何让AutoFixture从多个TypeRelay中随机选择?

moq - AutoData 理论与 AutoFixture 使用手动伪造

angular - 依赖注入(inject)抽象类 typescript (Angular2)

Spring 你可以在抽象类中 Autowiring 吗?