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

Jef*_*eff 6 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; }
}
Run Code Online (Sandbox Code Playgroud)

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

[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();
}
Run Code Online (Sandbox Code Playgroud)

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

System.InvalidCastException: Unable to cast object of type
'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.
Run Code Online (Sandbox Code Playgroud)

失败的演员阵容发生在我的ISpecimenBuilder实现中(显示在这个问题的底部),当我打电话给ISpecimenContext要解决Parent并且请求来自Child正在解决时.我可以防范来自Child解析操作的请求,如下所示:

//...
&& propertyInfo.ReflectedType != typeof(Child)
//...
Run Code Online (Sandbox Code Playgroud)

但是,这似乎污染了ISpecimenBuilder实施,知道'谁'可能正在提出请求.此外,它似乎复制了"全球" OmitOnRecursionBehavior的意图.

我想使用ISpecimenBuilder实例,因为除了处理循环引用之外,还有其他事情要自定义.我花了很多时间在SO上以及Ploeh上寻找这样的场景示例,但我还没有找到任何讨论行为和自定义组合的东西.重要的是,解决方案应该是我可以封装的解决方案ICustomization,而不是测试设置中的线和线

//...
fixture.ActLikeThis(new SpecialBehavior())
       .WhenGiven(typeof (Parent))
       .AndDoNotEvenThinkAboutBuilding(typeof(Child))
       .UnlessParentIsNull()
//...
Run Code Online (Sandbox Code Playgroud)

...因为最终我想扩展一个[AutoData]测试属性.

以下是我的ISpecimenBuilder实现和TracingBehavior失败测试的输出:

public class ChildSpecimenBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var propertyInfo = request as PropertyInfo;
        return propertyInfo != null
               && propertyInfo.PropertyType == typeof(Child)
                   ? Resolve(context)
                   : new NoSpecimen(request);
    }

    private static object Resolve(ISpecimenContext context)
    {
        var child = (Child) context.Resolve(typeof (Child));
        child.Name = context.Resolve(typeof (string)).ToString().ToLowerInvariant();
        child.Age = Math.Min(17, (int) context.Resolve(typeof (int)));
        return child;
    }
}

public class ParentSpecimenBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var propertyInfo = request as PropertyInfo;
        return propertyInfo != null
               && propertyInfo.PropertyType == typeof (Parent)
                   ? Resolve(context)
                   : new NoSpecimen(request);
    }

    private static object Resolve(ISpecimenContext context)
    {
        var parent = (Parent) context.Resolve(typeof (Parent));
        parent.Name = context.Resolve(typeof (string)).ToString().ToUpperInvariant();
        parent.Age = Math.Max(18, (int) context.Resolve(typeof (int)));
        return parent;
    }
}

CanCreatePatientGraphWithAutoFixtureManually(fixture: Ploeh.AutoFixture.Fixture) : Failed  Requested: Ploeh.AutoFixture.Kernel.SeededRequest
    Requested: CircularReference.Parent
      Requested: System.String Name
        Requested: Ploeh.AutoFixture.Kernel.SeededRequest
          Requested: System.String
          Created: 38ab48f4-b071-40f0-b713-ef9d4c825a85
        Created: Name38ab48f4-b071-40f0-b713-ef9d4c825a85
      Created: Name38ab48f4-b071-40f0-b713-ef9d4c825a85
      Requested: Int32 Age
        Requested: Ploeh.AutoFixture.Kernel.SeededRequest
          Requested: System.Int32
          Created: 9
        Created: 9
      Created: 9
      Requested: CircularReference.Child Child
        Requested: Ploeh.AutoFixture.Kernel.SeededRequest
          Requested: CircularReference.Child
            Requested: System.String Name
              Requested: Ploeh.AutoFixture.Kernel.SeededRequest
                Requested: System.String
                Created: 1f5ca160-b211-4f82-871f-11882dbcf00d
              Created: Name1f5ca160-b211-4f82-871f-11882dbcf00d
            Created: Name1f5ca160-b211-4f82-871f-11882dbcf00d
            Requested: Int32 Age
              Requested: Ploeh.AutoFixture.Kernel.SeededRequest
                Requested: System.Int32
                Created: 120
              Created: 120
            Created: 120
            Requested: CircularReference.Parent Parent
              Requested: CircularReference.Parent
              Created: Ploeh.AutoFixture.Kernel.OmitSpecimen

System.InvalidCastException: Unable to cast object of type 'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.
Run Code Online (Sandbox Code Playgroud)

Nik*_*nis 5

是否可以选择使用该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>())));
    }
}
Run Code Online (Sandbox Code Playgroud)

使用该属性的原始测试[ParentChildConventions]通过:

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