Заменить инъекцию в модуле Autofac с помощью объекта mock (Moq)
У меня есть модуль Autofac, как показано ниже
public class ServiceInjector:Module
{
protected override void Load(ContainerBuilder builder)
{
// many registrations and type looking up here
...
// One of the registration, say t which is found
// in above looking, is a resource consuming type
builder.RegisterType(t).As<ITimeConsume>();
// ...
}
}
И этот модуль используется в ServiceClass:
public class ServiceClass
{
static IContainer _ioc;
public ServiceClass()
{
var builder = new ContainerBuilder();
builder.RegisterModule<ServiceInjector>();
_ioc = builder.Build();
}
public void InvokeService()
{
using(var scope = _ioc.BeginLifetimeScope())
{
ITimeConsume obj = scope.Resolve<ITimeConsume>(...);
var result = obj.DoTimeConsumingJob(...);
// do something about result here ...
}
}
}
Мои вопросы: как мне проверить ServiceClass с помощью mocking (Moq) класса ITimeConsume? Здесь я пытаюсь написать тест ниже:
public void Test()
{
Mock<ITimeConsume> moc = GetMockObj(...);
// How can I inject moc.Object into ServiceInjector module,
// so that ServiceClass can use this mock object ?
}
Если, кстати, это невозможно, что может быть лучше для насмешки над классом, отнимающим много времени, который также может быть введен?
**
Обновить:
** Спасибо @dubs и подсказки @OldFox. Я думаю, что ключ в том, что инжектор Autofac должен быть инициализирован снаружи, а не внутренне контролироваться. Поэтому я использую возможности создания "на лету" Autofac.ILifetimeScope и проектирую конструктор ServiceClass с параметром области действия LifeTime. С этим дизайном я могу на лету регистрировать любую услугу в модульном тесте, как показано ниже:
using(var scope = Ioc.BeginLifetimeScope(
builder => builder.RegisterInstance(mockObject).As<ITimeConsume>())
2 ответа
В текущем проекте вы не можете внедрить свой макет объекта. Самое простое решение с наименьшими изменениями - это добавить Internal
Что до ServiceClass
:
internal ServiceClass(IContainer ioc)
{
_ioc = ioc;
}
Затем используйте атрибут InternalsVisibleTo
включить использование C`tor в вашем тестовом классе.
В фазе упорядочения / настройки /testInit инициализируйте тестируемый класс контейнером, содержащим фиктивный объект:
[SetUp]
public void TestInit()
{
Mock<ITimeConsume> moc = GetMockObj(...);
builder.RegisterInstance(moc).As<ITimeConsume>();
...
...
_target = new ServiceClass(builder.Build());
}
Лично у меня есть несколько экземпляров контейнера. Один для каждой конечной точки.
Тестовый проект
public class AutofacLoader
{
public static void Configure()
{
var builder = new ContainerBuilder();
builder.RegisterModule<ServiceProject.ServiceInjector>();
builder.RegisterModule<LocalTestProject.AutofacModule>();
Container = builder.Build();
}
public static IContainer Container { get; set; }
}
В этом случае локальный модуль автозапуска тестового проекта может переопределить модуль сервисного проекта с конкретными регистрациями.
Если более одного компонента предоставляют одну и ту же службу, Autofac будет использовать последний зарегистрированный компонент в качестве поставщика по умолчанию для этой службы: http://autofac.readthedocs.org/en/latest/register/registration.html
Тестовый класс
public void Test()
{
AutofacLoader.Configure();
var x = AutofacLoader.Container.Resolve<ITimeConsume>();
}