Решение проблемы внедрения зависимостей и модульное тестирование

StackOverflow https://stackoverflow.com/questions/820105

Вопрос

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

Я пишу консольное приложение, и контейнер создается и инициализируется в 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 это вызывает у меня проблемы в модульном тестировании, так как оно не было настроено.

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

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

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

Спасибо :-)

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

  • Акцент в современном модульном тестировании делается на дизайне
  • Вы должны свести к минимуму зависимость от конфигурации в модульных тестах.Конфигурацию можно протестировать с помощью smoke или интеграционных тестов.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top