Jak mogę używać niestandardowych programów ISpecimenBuilders z OmitOnRecursionBehavior?
Jak mogę użyć niestandardowegoISpecimenBuilder
instancje wraz zOmitOnRecursionBehavior
które chcę zastosować globalnie do wszystkich obiektów tworzonych przez urządzenia?
Pracuję z modelem EF Code First z cuchnącym odniesieniem kolistym, którego na potrzeby tego pytania nie można wyeliminować:
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; }
}
Jestem zaznajomiony z techniką odwołań cyklicznych, jak w tym teście:
[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();
}
Ale jeśli którekolwiek z obu dostosowań nie zostaną skomentowane, otrzymam wyjątek:
System.InvalidCastException: Unable to cast object of type
'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.
Upadająca obsada pojawia się w moimISpecimenBuilder
implementacje (pokazane na dole tego pytania), gdy dzwonię naISpecimenContext
rozwiązaćParent
a prośba pochodzi odChild
rozwiązany. Mógłbym wystrzegać się żądania pochodzącego odChild
rozwiązywanie operacji w ten sposób:
//...
&& propertyInfo.ReflectedType != typeof(Child)
//...
Ale to wydaje się zanieczyszczaćISpecimenBuilder
wdrożenie z wiedzą o tym, kto może złożyć wniosek. Wydaje się również, że powtarza pracę, którą „globalny”OmitOnRecursionBehavior
ma to zrobić.
Chcę użyćISpecimenBuilder
instancje, ponieważ mam inne rzeczy do dostosowania poza obsługą odwołania cyklicznego. Spędziłem dużo czasu szukając przykładów takiego scenariusza tutaj na SO, a także naPloeh ale nie znalazłem jeszcze niczego, co omawiapołączenie zachowań i dostosowań. Ważne jest, aby rozwiązanie było takie, z którym mogę się zająćICustomization
, zamiast linii i linii w konfiguracji testu
//...
fixture.ActLikeThis(new SpecialBehavior())
.WhenGiven(typeof (Parent))
.AndDoNotEvenThinkAboutBuilding(typeof(Child))
.UnlessParentIsNull()
//...
... ponieważ ostatecznie chcę przedłużyć[AutoData]
atrybut do testów.
Co następuje, to mojeISpecimenBuilder
implementacje i wyjścieTracingBehavior
w przypadku niepowodzenia testu:
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'.