IList<something> constructor parameter and AutoFixture
Using autofixture, I'm开发者_如何学Python trying to construct anonymous instance of Project
:
_f=new Fixture().Customize(new AutoMoqCustomization());
_p=_f.CreateAnonymous<Project>();
This fails, cause Project
public constructor demands IList<Partner>
public Project(/*.....*/,IList<Partner> partners){
Guard.AgainstEmpty(partners);
}
Stack trace isn't meaningful (at least - for me). Just some reflection yada-yada:
failed: System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
---- System.ArgumentException : Value does not fall within the expected range. at System.RuntimeMethodHandle._InvokeConstructor(IRuntimeMethodInfo method, Object[] args, SignatureStruct& signature, RuntimeType declaringType)
So - how to make sure autoFixture passses in anonymous collection of partners in order to construct it?
It's not fault of IList<Partners>
. There's another parameter called Priority
. Priority
itself holds Measure
, Measure
holds IList<Indicator>
and calls Guard.AgainstEmpty(indicators)
in constructor.
So it looks something like this:
fixture.CreateAnonymous<Foo>(); //kaboom!
public class Foo{
public Foo(IList<Bar> bars){
Guard.AgainstEmpty(bars); //just checks count for ienumerable & throws if 0
Bars=bars;
}
public IList<Bar> Bars {get;private set;} //should be readonly collection...
}
public class Fizz{
public Fizz(Foo foo){
Foo=foo;
}
public Foo{get;private set;}
}
public class Bar{}
Construction fails in Guard.AgainstEmpty
method. So - the question becomes - how to make sure AutoFixture fills some bars in bars collection before constructing foos?
This helps. Browsing source often helps.
var indicators=_f.CreateMany<Indicator>();
_f.Register<IList<Indicator>>(()=>indicators.ToList());
There might be better way though.
In overall, this is how it looks at the moment:
_f=new Fixture().Customize(new AutoMoqCustomization());
var indicators=_f.CreateMany<Indicator>();
_f.Register<IList<Indicator>>(()=>indicators.ToList());
var regionName=_f.CreateAnonymous<string>();
_f.Register<string,Country,bool,Region>((name,country,call)=>
new Region(regionName,_f.CreateAnonymous<Country>(),true));
_c.Set(x=>x.Regions,_f.CreateMany<Region>().ToList());
_f.Register<IList<ManagementBoardEntry>>(()=>
_f.CreateMany<ManagementBoardEntry>().ToList());
_f.Register<IList<FinancialInfoEntry>>(()=>
_f.CreateMany<FinancialInfoEntry>().ToList());
_f.Register<IList<Partner>>(()=>_f.CreateMany<Partner>().ToList());
_p=_f.CreateAnonymous<Project>();
Can't call that beautiful (any refactoring suggestions are welcome), but it's still much better than writing everything manually.
Using IList
there is a wrong choice for sure. Even worse - I'm using IList
for properties too. That invites client to use them directly instead of going through aggregate root.
There is one drawback when using params
. Can't use more than one (unless I'm missing some basics again). And I'm receiving list as an input (part of excel sheet DOM), Can't know compile time how much elements will be there.
Model is really fresh. Just baked it (so there is great chance that I'm wrong about those emptiness checks, will talk with client and business analyst about that).
My strategy is to freely sculpture it and push it towards desired state with unit tests. This is actual reason I dislike strict TDD a bit. It steals focus, forces me to think about details instead of whole picture kinda too soon. I prefer to sketch it and refine until it looks good. But that might be cause I'm not fluent enough with testing.
Anyway - thank You for great tips. I will continue to learn more about AutoFixture.
精彩评论