Какой должна быть стратегия юнит-тестирования при использовании IoC? - PullRequest
16 голосов
/ 21 февраля 2010

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

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

Ответы [ 4 ]

20 голосов
/ 21 февраля 2010

Вам не нужен DI-контейнер в модульных тестах, потому что зависимости предоставляются через фиктивные объекты, сгенерированные с помощью таких структур, как 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 в конструкторе, который сам может иметь зависимости от других объектов, в то время как в модульном тесте вы создаете фиктивный объект, потому что вы хотите только протестировать этот класс в изоляции. Пример с Mhino Rhino:

[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);
}
3 голосов
/ 21 февраля 2010

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

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

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

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

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

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

2 голосов
/ 21 февраля 2010

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

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

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

0 голосов
/ 21 февраля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...