Подтвердить что ты не робот

Какова должна быть стратегия модульного тестирования при использовании IoC?

В конце концов, что я прочитал о Injection Dependency и IoC, я решил попробовать использовать Windsor Container в нашем приложении (это многослойное веб-приложение 50K LOC, поэтому я надеюсь, что это не перебор). Я использовал простой статический класс для упаковки контейнера, и я инициализирую его при запуске приложения, которое сейчас работает отлично.

Мой вопрос об модульном тестировании. Я знаю, что DI собирается сделать мою жизнь намного легче там, предоставив мне возможность впрыскивать программы-заглушки/макеты учеников класса в тестируемый класс. Я уже написал пару тестов, используя эту технику, и, похоже, это имеет смысл для меня. Я не уверен в том, должен ли я использовать IoC (в данном случае Windsor Castle) также в модульных тестах (возможно, как-то настроить его для возврата заглушек/макетов для моих особых случаев) или лучше подключить все зависимости вручную в тестах. Как вы думаете, и какая практика сработала для вас?

4b9b3361

Ответ 1

Вам не нужен контейнер DI в модульных тестах, потому что зависимости предоставляются через mock-объекты, созданные с помощью фреймворков, таких как Rhino Mocks или Moq. Например, когда вы тестируете класс, который имеет зависимость от некоторого интерфейса, эта зависимость обычно предоставляется через инъекцию конструктора.

public class SomeClassToTest
{
    private readonly ISomeDependentObject _dep;
    public SomeClassToTest(ISomeDependentObject dep)
    {
        _dep = dep;
    }

    public int SomeMethodToTest()
    {
        return _dep.Method1() + _dep.Method2();
    }
}

В вашем приложении вы будете использовать структуру DI, чтобы передать некоторую реальную реализацию ISomeDependentObject в конструкторе, которая сама могла бы иметь зависимости от других объектов, а в unit test вы создаете макет-объект, потому что вы хотите протестировать это класс в изоляции. Пример с Rhino Mocks:

[TestMethod]
public void SomeClassToTest_SomeMethodToTest()
{
    // arrange
    var depStub = MockRepository.CreateStub<ISomeDependentObject>();
    var sut = new SomeClassToTest(depStub);
    depStub.Stub(x => x.Method1()).Return(1);
    depStub.Stub(x => x.Method2()).Return(2);

    // act
    var actual = sut.SomeMethodToTest();

    // assert
    Assert.AreEqual(3, actual);
}

Ответ 2

Я работаю над проектом ASP.NET MVC с примерно 400 модульными тестами. Я использую Ninject для инъекции зависимостей и MBUnit для тестирования.

Ninject не требуется для модульного тестирования, но он уменьшает количество кода, который я должен ввести. Мне нужно указать только один раз (для каждого проекта), как мои интерфейсы должны создаваться, а не делать это каждый раз, когда я инициализирую тестируемый класс.

Чтобы сэкономить время при написании новых тестов, я создал базовые классы test fixture с максимально возможным общим кодом настройки. Процедуры установки в этих классах инициализируют поддельные репозитории, создают некоторые тестовые данные и поддельные идентификаторы для тестового пользователя. Модульные тесты только инициализируют данные, которые слишком специфичны для перехода на общие процедуры настройки.

Я также высмеиваю объекты (в отличие от фальсификации) в некоторых тестах, но я обнаружил, что поддельные хранилища данных приводят к меньшему количеству работы и более точных тестов.

Например, было бы труднее проверить, правильно ли проверяет метод, который я правильно фиксировал все обновления в репозитории после их создания при использовании фальшивки репозитория, чем когда я использую фальшивку репозитория.

Вначале было очень немного работы, но на самом деле помогло мне сэкономить много времени в долгосрочной перспективе.

Ответ 3

Я только что написал очень похожее приложение стиля и размера. Я бы не вложил никакой инъекции зависимостей в модульные тесты, потому что он недостаточно сложный, чтобы быть необходимым. Вы должны использовать насмешливую структуру для создания своих макетов (RhinoMocks/Moq).

Также Automocking в Moq или Auto Mock Container в Rhinomocks упростит дальнейшее развитие ваших макетов.

Автоматическое издевательство позволяет вам получить объект типа, который вы хотите проверить без настраивая макеты вручную. Все зависимости автоматически высмеиваются (предполагая, что они являются интерфейсами) и вводится в конструктор типов. Если вам нужно настроить ожидаемые поведение, но вам не нужно.

Ответ 4

Как уже указывал Дарин, вам не нужно использовать DI, если у вас есть издевательства. (Тем не менее, у DI есть и другие преимущества, в том числе, в первую очередь, снижение зависимостей вашего кода, что делает ваш код намного проще в обслуживании и расширении в конечном итоге.)

Я лично предпочитаю подключать все в своих модульных тестах, тем самым полагаясь как можно меньше на внешние фреймворки, файлы конфигурации и т.д.