6

如何使用自定义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(显示在这个问题的底部) 。我可以防止来自解析操作的请求,如下所示:ISpecimenContextParentChildChild

//...
&& propertyInfo.ReflectedType != typeof(Child)
//...

但是,这似乎污染了ISpecimenBuilder执行,因为知道“谁”可能会提出请求。此外,它似乎重复了“全球”本OmitOnRecursionBehavior应做的工作。

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

//...
fixture.ActLikeThis(new SpecialBehavior())
       .WhenGiven(typeof (Parent))
       .AndDoNotEvenThinkAboutBuilding(typeof(Child))
       .UnlessParentIsNull()
//...

...因为最终我想[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'.
4

2 回答 2

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();
}
于 2013-09-03T21:12:30.910 回答
2

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

于 2015-12-11T14:24:40.803 回答