Разрешение внедрения зависимостей и модульное тестирование - PullRequest
6 голосов
/ 04 мая 2009

Я пытаюсь научиться внедрять зависимости и столкнулся с проблемой при модульном тестировании приложения.

Я пишу консольное приложение, и контейнер создается и инициализируется в Main (), он доступен как get-property в Program.Container, поэтому в любом месте моего приложения я могу вызвать Program.Container.Resolve<..>().

У меня есть класс ServiceValidator, такой как:

public class ServiceValidator
{
    private readonly IConfiguration _configuration;
    private readonly IService _service;

    public ServiceValidator(IConfiguration configuration, IService service)
    {
        _configuration = configuration;
        _service = service;
    }

В другом классе я использую

ServiceValidator serviceValidator = Program.Container.Resolve<ServiceValidator>();
serviceValidator.VerifyVersion();

Это вызов Program.Container.Resolve, который вызывает у меня проблемы в модульном тесте, так как он не был настроен.

Это плохая практика, вызывать решимость на контейнере? Я мог бы создать экземпляр ServiceValidator в Main() и передать объект, но это кажется глупым, поскольку это вызвало бы множество параметров для объектов, которые просто передаются следующему методу.

Так что, я думаю, допустимо вызывать Resolve внутри класса, но тогда контейнер должен быть настроен для модульного теста. Как мне это сделать, я должен переместить контейнер в другое место, чем класс Program? Что бы вы порекомендовали?

Если это имеет значение, я использую Unity и C #

Спасибо: -)

Ответы [ 5 ]

8 голосов
/ 04 мая 2009

Это плохая практика, вызывать решимость для контейнера? Я мог бы создать экземпляр ServiceValidator в Main () и передать объект, но это кажется глупым, поскольку это вызвало бы множество параметров для объектов, которые просто передаются следующему методу.

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

Таким образом, если у вас есть класс X, для которого требуется ServiceValidator, то класс X будет иметь параметр конструктора типа ServiceValidator. Тогда, если некоторый класс Y использует класс X, тогда класс Y будет иметь параметр конструктора типа X. Обратите внимание, что Y ничего не знает о ServiceValidator, поэтому вам не нужно передавать ServiceValidator из одного класса в другой - единственное место, где он используется, - это конструирование X, и это часто делается с помощью DI-фреймворка или только в одном месте на фабрике, написанной от руки.

Некоторые ссылки для получения дополнительной информации:

1 голос
/ 04 мая 2009

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

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

Я также использую Microsoft Service Locator , чтобы моя зависимость зависела от чего-то из .NET Framework, а не от конкретного контейнера. Это позволяет мне в будущем использовать все, что я захочу, даже домашний вагон.

0 голосов
/ 04 мая 2009

Проблема заключается в том, что вы пытаетесь протестировать метод Main. Этот метод практически невозможно для модульного тестирования.

Я бы сказал, что лучше не тестировать ваш метод Main, потому что:

  • Акцент современного модульного тестирования - на дизайне
  • Вы должны минимизировать зависимость от конфигурации в модульных тестах. Конфигурация может быть проверена с помощью дымовых или интеграционных испытаний.
0 голосов
/ 04 мая 2009

Хорошо, что вы технически делаете, это место обслуживания в вашем классе.

Я помню, как читал эту статью некоторое время назад:

http://martinfowler.com/articles/injection.html

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

0 голосов
/ 04 мая 2009

Вы можете использовать статический класс в качестве инициализатора для вашего контейнера. Что-то вроде BootStrapper.cs будет хорошо. Затем вы можете ссылаться на методы класса как в своем коде, так и в тестах.

...