.NET Unit Test без конструктора без параметров, чтобы облегчить инъекцию зависимостей

Я пытаюсь, чтобы модульные тесты не полагались на вызов container.Resolve <T> () для их зависимостей.

В настоящее время я использую AutoFac 2.2.4 и пробовал xUnit.NET и NUnit, но оба имеют эту проблему

Нет конструктора без параметров для этого объекта

Как мне пройти эту проблему? Является ли это конкретной платформой тестирования модулей, которая будет поддерживать это, или как именно настроена эта структура?

Должен ли я не делать этого? Или я могу настроить тестовый класс для работы с конструктором, у которого есть только зависимость?

Вот некоторые из кода:

public class ProductTests : BaseTest
{
    readonly private IProductRepository _repo;

    public ProductTests(IProductRepository r)
    {
        _repo = r;
    }

    //working unit tests here with default constructor
} 

Я решил неправильно инициализировать контейнер в конструкторе базового класса?

public abstract class BaseTest
{
    protected BaseTest()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ProductRepository>().As<IProductRepository>();
        builder.Build();
    }
}

Ответ 1

Исходная проблема действительно связана с тем, как разрабатываются схемы тестирования. Все они требуют конструктора без параметров, чтобы создать экземпляр тестовых экземпляров. И это справедливо. В этих структурах конструктор не должен полагаться на инициализацию теста. Это цель метода SetUp. В общем, сами тесты не подходят для инъекций.

И ИМО, это становится проблемой, когда вы разрабатываете тесты, чтобы не зависеть от контейнера. В конце концов, каждый тестовый класс должен сосредоточиться на одной "тестируемой системе" (SUT). Почему бы вам не создать метод установки, чтобы создать эту систему напрямую и обеспечить каждую зависимость (обычно в виде подделок)? Делая это таким образом, вы эффективно удалили другую ненужную зависимость от своих тестов, а именно структуру IoC.

С одной стороны: единственный раз, когда я использую структуру IoC в своих тестах, в моих "контейнерных тестах". Эти тесты направлены на проверку того, что определенные сервисы могут быть разрешены из контейнера после инициализации контейнера с помощью приложений или модулей сборки.

Ответ 2

Я просто разрешаю моим испытаниям иметь зависимость от Autofac, хотя я их инкапсулирую. Все мои TestFixtures наследуются от Fixture, который определяется как таковой:

public class Fixture
{
    private static readonly IContainer MainContainer = Ioc.Build();
    private readonly TestLifetime _testLifetime = new TestLifetime(MainContainer);

    [SetUp]
    public void SetUp()
    {
        _testLifetime.SetUp();
    }

    [TearDown]
    public void TearDown()
    {
        _testLifetime.TearDown();
    }

    protected TService Resolve<TService>()
    {
        return _testLifetime.Resolve<TService>();
    }

    protected void Override(Action<ContainerBuilder> configurationAction)
    {
        _testLifetime.Override(configurationAction);
    }
}

public class TestLifetime
{
    private readonly IContainer _mainContainer;

    private bool _canOverride;
    private ILifetimeScope _testScope;

    public TestLifetime(IContainer mainContainer)
    {
        _mainContainer = mainContainer;
    }

    public void SetUp()
    {
        _testScope = _mainContainer.BeginLifetimeScope();
        _canOverride = true;
    }

    public void TearDown()
    {
        _testScope.Dispose();
        _testScope = null;
    }

    public TService Resolve<TService>()
    {
        _canOverride = false;
        return _testScope.Resolve<TService>();
    }

    public void Override(Action<ContainerBuilder> configurationAction)
    {
        _testScope.Dispose();

        if (!_canOverride)
            throw new InvalidOperationException("Override can only be called once per test and must be before any calls to Resolve.");

        _canOverride = false;
        _testScope = _mainContainer.BeginLifetimeScope(configurationAction);
    }
}