Заменить инъекцию в модуле 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>();
}
Другие вопросы по тегам