Wie kann ich benutzerdefinierte ISpecimenBuilder mit OmitOnRecursionBehavior verwenden?
Wie kann ich benutzerdefinierte verwendenISpecimenBuilder
Instanzen zusammen mit derOmitOnRecursionBehavior
Was möchte ich global auf alle Fixture-Objekte anwenden?
Ich arbeite mit einem EF Code First-Modell mit einem übelriechenden Zirkelverweis, der für die Zwecke dieser Frage nicht beseitigt werden kann:
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; }
}
Ich kenne mich mit der Technik für Zirkelverweise aus, wie in diesem bestandenen Test:
[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();
}
Wenn eine oder beide Anpassungen nicht kommentiert sind, erhalte ich eine Ausnahme:
System.InvalidCastException: Unable to cast object of type
'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.
Die fehlgeschlagene Besetzung tritt in meinerISpecimenBuilder
Implementierungen (am Ende dieser Frage gezeigt), wenn ich auf dieISpecimenContext
lösenParent
und die Anfrage kommt von derChild
gelöst werden. Ich könnte mich gegen die Bitte von der schützenChild
Vorgang wie folgt lösen:
//...
&& propertyInfo.ReflectedType != typeof(Child)
//...
Aber das scheint die Umwelt zu verschmutzenISpecimenBuilder
Implementierung mit dem Wissen, von wem die Anfrage stammt. Auch scheint es die Arbeit zu duplizieren, die die "globale"OmitOnRecursionBehavior
soll tun.
Ich möchte das benutzenISpecimenBuilder
Instanzen, weil ich neben der Zirkelreferenz noch andere Anpassungen vornehmen muss. Ich habe viel Zeit damit verbracht, nach Beispielen für ein solches Szenario hier auf SO und auch auf zu suchenPloeh aber ich habe noch nichts gefunden, das das besprichtKombination von Verhalten und Anpassungen. Es ist wichtig, dass die Lösung eine ist, mit der ich kapseln kannICustomization
, anstatt Zeilen und Zeilen im Testaufbau von
//...
fixture.ActLikeThis(new SpecialBehavior())
.WhenGiven(typeof (Parent))
.AndDoNotEvenThinkAboutBuilding(typeof(Child))
.UnlessParentIsNull()
//...
... weil ich letztendlich einen verlängern will[AutoData]
Attribut für Tests.
Was folgt, sind meineISpecimenBuilder
Implementierungen und die Ausgabe derTracingBehavior
für den nicht bestandenen Test:
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'.