How does the Mapjecter Inject structure work?

I have a constructor containing an IEnumerable parameter. When I try to embed a specific object in automocker, it is not used.

When I use a wrapper class that contains the IEnumerable property, everything works as expected.

How can I test TestClass1?

IEnumerable parameter

public class TestClass1 { public TestClass1(IEnumerable<IItem> items) { Items = items; } public IEnumerable<IItem> Items { get; private set; } } [TestMethod] public void TestClass1Constructor() { RhinoAutoMocker<TestClass1> autoMocker = new RhinoAutoMocker<TestClass1>(); IEnumerable<IItem> items = new[] { MockRepository.GenerateMock<IItem>() }; autoMocker.Inject(items); Assert.AreEqual(1, autoMocker.ClassUnderTest.Items.Count()); } 

Test result:

Error Assert.AreEqual. Expected: <1>. Actual :. & L; 0>

Class parameter Wrapper

 public class TestClass2 { public TestClass2(WrapperClass numbersWrapper) { Items = numbersWrapper.Items; } public IEnumerable<IItem> Items { get; private set; } } [TestMethod] public void TestClass2Constructor() { RhinoAutoMocker<TestClass2> autoMocker = new RhinoAutoMocker<TestClass2>(); WrapperClass numbers = new WrapperClass(new[] { MockRepository.GenerateMock<IItem>() }); autoMocker.Inject(numbers); Assert.AreEqual(1, autoMocker.ClassUnderTest.Items.Count()); } 

Test result:

Success

+2
source share
1 answer

After looking at the source code for the AutoMocker<TTargetClass> class , I noticed the following:

  • AutoMocker uses StructureMap container under covers
  • usually all dependencies are directly resolved using the container
  • IEnumerable<T> type dependencies are handled differently (see below)

Here's a snippet of code from the AutoMocker<TTargetClass> class that shows how constructor dependencies are solved (I cut several lines a bit):

 private object[] getConstructorArgs() { ConstructorInfo ctor = Constructor.GetGreediestConstructor(typeof (TTargetClass)); var list = new List<object>(); foreach (ParameterInfo parameterInfo in ctor.GetParameters()) { Type dependencyType = parameterInfo.ParameterType; if (dependencyType.IsArray) { [...] } else if (dependencyType.Closes(typeof (IEnumerable<>))) { Type @interface = dependencyType.FindFirstInterfaceThatCloses(typeof (IEnumerable<>)); Type elementType = @interface.GetGenericArguments().First(); // Here the interesting code: var builder = typeof (EnumerableBuilder<>).CloseAndBuildAs<IEnumerableBuilder>(_container, elementType); list.Add(builder.ToEnumerable()); } else { object dependency = _container.GetInstance(dependencyType); list.Add(dependency); } } return list.ToArray(); } 

The code shows that dependencies are usually resolved using _container.GetInstance , but there are two exceptions: ararys and IEnumerable<> s

In the case of IEnumerable<T> , it turns out that _container.GetAllInstances(typeof(T)) . This means that in your case you should introduce multiple instances of IItem , not IEnumerable<IItem> . The code responsible for this is the EnumerableBuilder<T> class, which can be found in the same file (at the end).

Ok, enough talk. I'm not sure my explanations are clear enough, so the code below is for the two tests that pass. Hope this clarifies everything:

 [Test] public void Test_OneItem() { var autoMocker = new RhinoAutoMocker<TestClass1>(); autoMocker.Inject(MockRepository.GenerateMock<IItem>()); Assert.AreEqual(1, autoMocker.ClassUnderTest.Items.Count()); } [Test] public void Test_TwoItems() { var autoMocker = new RhinoAutoMocker<TestClass1>(); autoMocker.Inject(MockRepository.GenerateMock<IItem>()); autoMocker.Inject(MockRepository.GenerateMock<IItem>()); Assert.AreEqual(2, autoMocker.ClassUnderTest.Items.Count()); } 
+2
source

All Articles