How Structure Map Automocker Inject works?

☆樱花仙子☆ 提交于 2020-01-04 12:47:41

问题


I have constructor containing IEnumerable parameter. When I try to Inject concrete object to automocker it is not used.

When I use wrapper class containing IEnumerable property all 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());
}

Result of the test is:

Assert.AreEqual failed. Expected:<1>. Actual:<0>.

Wrapper class parameter

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());
}

Result of the test is:

Success.


回答1:


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

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

Here's a piece of code from the AutoMocker<TTargetClass> class that shows how constructor dependencies are resolved (I removed some lines for brevity):

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's 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

For IEnumerable<T>, it turns out that _container.GetAllInstances(typeof(T)) is used. This meas that in your case you should inject several IItem instances, not an IEnumerable<IItem>. The code responsible for this is the EnumerableBuilder<T> class, whih can be found in the same file (at the end).

OK, enough talk. I'm not sure if my explanation is clear enough, so below is code for two tests that pass. Hopefully that will clarify 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());
}


来源:https://stackoverflow.com/questions/15761090/how-structure-map-automocker-inject-works

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!