Как я могу использовать собственные ISpecimenBuilders с OmitOnRecursionBehavior?

Как я могу использовать пользовательские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 и запрос исходит отChild решается. Я мог бы остерегаться просьбы отChild разрешаем операцию так:

//...
&& 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'.

Ответы на вопрос(2)

Ваш ответ на вопрос