如何将自定义ISpecimenBuilders与OmitOnRecursionBehavior一起使用?

时间:2013-09-03 18:03:43

标签: autofixture

如何将自定义ISpecimenBuilder实例与我想要全局应用的OmitOnRecursionBehavior一起用于所有夹具创建的对象?

我正在使用带有恶臭循环引用的EF Code First模型,出于此问题的目的,无法消除:

public class Parent {
    public string Name { get; set; }
    public int Age { get; set; }
    public virtual Child Child { get; set; }
}

public class Child {
    public string Name { get; set; }
    public int Age { get; set; }
    public virtual Parent Parent { get; set; }
}

我熟悉侧步循环引用的技术,就像在这个通过测试中一样:

[Theory, AutoData]
public void CanCreatePatientGraphWithAutoFixtureManually(Fixture fixture)
{
    //fixture.Customizations.Add(new ParentSpecimenBuilder());
    //fixture.Customizations.Add(new ChildSpecimenBuilder());
    fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
                     .ForEach(b => fixture.Behaviors.Remove(b));
    fixture.Behaviors.Add(new OmitOnRecursionBehavior());
    fixture.Behaviors.Add(new TracingBehavior());
    var parent = fixture.Create<Parent>();
    parent.Should().NotBeNull();
    parent.Child.Should().NotBeNull();
    parent.Child.Parent.Should().BeNull();
}

但如果其中一个/两个自定义都被取消注释,我会得到一个例外:

System.InvalidCastException: Unable to cast object of type
'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.

当我致电ISpecimenBuilder以解决ISpecimenContext并且请求来自{{}时,我的Parent实施(在此问题的底部显示)中发生了失败的广告{1}}正在解决。我可以防范来自Child解析操作的请求,如下所示:

Child

但是,这似乎污染了//... && propertyInfo.ReflectedType != typeof(Child) //... 实施,知道'谁'可能正在提出请求。此外,它似乎复制了“全局”ISpecimenBuilder的目的。

我想使用OmitOnRecursionBehavior实例,因为除了处理循环引用之外,还有其他事情要自定义。我花了很多时间在SO上和Ploeh上查找这样的场景示例,但我还没有找到任何讨论行为和自定义组合的内容。重要的是,解决方案是我可以用ISpecimenBuilder封装的,而不是

的测试设置中的行和行。
ICustomization

...因为最终我想为测试扩展//... fixture.ActLikeThis(new SpecialBehavior()) .WhenGiven(typeof (Parent)) .AndDoNotEvenThinkAboutBuilding(typeof(Child)) .UnlessParentIsNull() //... 属性。

以下是我的[AutoData]实施以及失败测试的ISpecimenBuilder输出:

TracingBehavior

2 个答案:

答案 0 :(得分:4)

是否可以使用Customize方法自定义创建算法?

如果是,您可以创建并使用以下[ParentChildConventions]属性:

internal class ParentChildConventionsAttribute : AutoDataAttribute
{
    internal ParentChildConventionsAttribute()
        : base(new Fixture().Customize(new ParentChildCustomization()))
    {
    }
}

internal class ParentChildCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Child>(c => c
            .With(x => x.Name,
                fixture.Create<string>().ToLowerInvariant())
            .With(x => x.Age,
                Math.Min(17, fixture.Create<int>()))
            .Without(x => x.Parent));

        fixture.Customize<Parent>(c => c
            .With(x => x.Name,
                fixture.Create<string>().ToUpperInvariant())
            .With(x => x.Age,
                Math.Min(18, fixture.Create<int>())));
    }
}

原始测试使用[ParentChildConventions]属性传递:

[Theory, ParentChildConventions]
public void CanCreatePatientGraphWithAutoFixtureManually(
    Parent parent)
{
    parent.Should().NotBeNull();
    parent.Child.Should().NotBeNull();
    parent.Child.Parent.Should().BeNull();
}

答案 1 :(得分:1)

您也可以使用AutoFixture.AutoEntityFramework来帮助EF。