Как работает карта структуры Automocker Inject?
У меня есть конструктор, содержащий параметр IEnumerable. Когда я пытаюсь ввести конкретный объект в автомокер, он не используется.
Когда я использую класс-оболочку, содержащую свойство IEnumerable, все работает как положено.
Как я могу проверить TestClass1?
IEnumerable параметр
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());
}
Результат теста:
Assert.AreEqual не удалось. Ожидаемое:<1>. Фактическое:<0>.
Параметр класса 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());
}
Результат теста:
Успех.
1 ответ
Посмотрев на исходный код AutoMocker<TTargetClass>
класс, я заметил следующее:
- AutoMocker использует контейнер StructureMap под крышками
- обычно все зависимости решаются напрямую с помощью контейнера
- зависимости, которые имеют тип
IEnumerable<T>
обрабатываются по-разному (см. ниже)
Вот кусок кода из AutoMocker<TTargetClass>
Класс, который показывает, как разрешаются зависимости конструктора (для краткости я удалил несколько строк):
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();
}
Код показывает, что зависимости обычно разрешаются с помощью _container.GetInstance
, но есть два исключения: ararys и IEnumerable<>
s
За IEnumerable<T>
, Оказывается, что _container.GetAllInstances(typeof(T))
используется. Это означает, что в вашем случае вы должны ввести несколько IItem
случаи, а не IEnumerable<IItem>
, Код, ответственный за это EnumerableBuilder<T>
класс, который можно найти в том же файле (в конце).
ОК, хватит разговоров. Я не уверен, что мое объяснение достаточно ясное, поэтому ниже приведен код для двух пройденных тестов. Надеюсь, это все прояснит:
[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());
}