views:

58

answers:

1

Using autofixture, I'm 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?

+1  A: 

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.

Arnis L.
+1 Mapping to CreateMany is currently the idiomatic way to populate a list, although you can do it in a single statement like this: `_f.Register<IList<Indicator>>(() => _f.CreateMany<Indicator>().ToList());`
Mark Seemann
However, see also: http://autofixture.codeplex.com/workitem/4199
Mark Seemann
@Mark did I say that Your tool saved my day? that Project class holds tons of value objects God only knows how deep. it would take hours just to setup valid project.
Arnis L.
As an overall consideration though: AutoFixture is based on a set of conventions that makes stuff easy. When it becomes difficult to customize AutoFixture, it's a smell that you should reconsider your design. In this case, for example, consider whether the 'not-empty' precondition really makes sense. Usually when we work with lists, it's expected that they can have any length, including zero, so going against that is violating POLA.
Mark Seemann
The above is just meant as a friendly nudge :)
Mark Seemann
However, if you truly must have at least one instance in a list, consider an alternative constructor like this: `public Project(Partner partner, params Partner[] partners)` This forces all consumers of your API (including AutoFixture) to supply at least one Partner. This helps AutoFixture correctly wire everything, but it also helps all other developers (including your future self) consuming your API correctly :)
Mark Seemann
@Mark updated answer as a reply to Your comments.
Arnis L.
One more comment about the use of params arrays: you are correct that you can only have one params array per ctor, but consider what it means if you feel the need to have more: does it smell of an SRP violation? It might :)
Mark Seemann