¿Cómo puedo usar ISpecimenBuilders personalizados con OmitOnRecursionBehavior?
¿Cómo puedo usar la costumbre?ISpecimenBuilder
instancias junto con elOmitOnRecursionBehavior
¿Qué deseo aplicar globalmente a todos los objetos creados por aparatos?
Estoy trabajando con un modelo de EF Code First con una referencia circular de olor desagradable que, para los fines de esta pregunta, no se puede eliminar:
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; }
}
Estoy familiarizado con la técnica para referencias circulares de pasos laterales, como en esta prueba que pasa:
[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();
}
Pero si cualquiera o ambas personalizaciones no tienen comentarios, obtendré una excepción:
System.InvalidCastException: Unable to cast object of type
'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.
El elenco defectuoso está ocurriendo en miISpecimenBuilder
implementaciones (que se muestran en la parte inferior de esta pregunta) cuando llamo alISpecimenContext
resolverParent
y la solicitud viene de laChild
siendo resuelto Podría protegerme contra la petición que venía de laChild
Resolviendo la operación de esta manera:
//...
&& propertyInfo.ReflectedType != typeof(Child)
//...
Pero, eso parece contaminar elISpecimenBuilder
Implementación con conocimiento de 'quién' podría estar haciendo la solicitud. Además, parece duplicar el trabajo que el 'global'OmitOnRecursionBehavior
está destinado a hacer
Quiero usar elISpecimenBuilder
instancias porque tengo otras cosas para personalizar además de manejar la referencia circular. He pasado mucho tiempo buscando ejemplos de un escenario como este aquí en SO y también enPloeh pero no he encontrado nada todavía que discute laCombinación de comportamientos y personalizaciones.. Es importante que la solución sea una que pueda encapsular conICustomization
, en lugar de líneas y líneas en la configuración de prueba de
//...
fixture.ActLikeThis(new SpecialBehavior())
.WhenGiven(typeof (Parent))
.AndDoNotEvenThinkAboutBuilding(typeof(Child))
.UnlessParentIsNull()
//...
... porque en última instancia quiero extender un[AutoData]
atributo para pruebas.
Lo que sigue son misISpecimenBuilder
implementaciones y la salida de laTracingBehavior
para la prueba que falla
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'.