autofixture - How can I use custom ISpecimenBuilders with OmitOnRecursionBehavior? -
how can use custom ispecimenbuilder
instances along omitonrecursionbehavior
want applied globally fixture-created objects?
i'm working ef code first model foul-smelling circular reference that, purposes of question, cannot eliminated:
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; } }
i'm familiar technique side-stepping circular references, in passing 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(); }
but if either/both customizations uncommented, exception:
system.invalidcastexception: unable cast object of type 'ploeh.autofixture.kernel.omitspecimen' type 'circularreference.parent'.
the failing cast occurring in ispecimenbuilder
implementations (shown @ bottom of question) when call on ispecimencontext
resolve parent
, request coming child
being resolved. guard against request coming child
resolving operation this:
//... && propertyinfo.reflectedtype != typeof(child) //...
but, seems pollute ispecimenbuilder
implementation knowledge of 'who' might making request. also, seems duplicate work 'global' omitonrecursionbehavior
meant do.
i want use ispecimenbuilder
instances because have other things customize besides handling circular reference. i've spent lot of time looking examples of scenario here on , on ploeh haven't found yet discusses combination of behaviors , customizations. it's important solution 1 can encapsulate icustomization
, rather lines , lines in test setup of
//... fixture.actlikethis(new specialbehavior()) .whengiven(typeof (parent)) .anddonoteventhinkaboutbuilding(typeof(child)) .unlessparentisnull() //...
...because want extend [autodata]
attribute tests.
what follows ispecimenbuilder
implementations , output of tracingbehavior
failing test:
public class childspecimenbuilder : ispecimenbuilder { public object create(object request, ispecimencontext context) { var propertyinfo = request 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 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 cast object of type 'ploeh.autofixture.kernel.omitspecimen' type 'circularreference.parent'.
is option customize creation algorithm using customize
method?
if yes, can create , use following [parentchildconventions]
attribute:
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>()))); } }
the original test, using [parentchildconventions]
attribute, passes:
[theory, parentchildconventions] public void cancreatepatientgraphwithautofixturemanually( parent parent) { parent.should().notbenull(); parent.child.should().notbenull(); parent.child.parent.should().benull(); }
Comments
Post a Comment