Как я могу перепроектировать свое приложение C #, чтобы избежать перегрузки инжектора конструктора? - PullRequest
1 голос
/ 25 октября 2019

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

Как я уже говорил, список зависимостей продолжает расти и расти. У меня есть другие веб-службы для вызова, серверы SQL и базы данных DB2 для взаимодействия, файл конфигурации для чтения, журнал для записи и чтение данных из Sharepoint. Пока у меня есть 10 зависимостей. И каждый раз, когда я добавляю один, он ломает все мои модульные тесты, так как в конструкторе есть новый параметр.

Если это поможет, я использую .Net 4.5, Castle Windsor в качестве своего IOC, MSTest и Moq для тестирования.

Я посмотрел здесь Как избежать безумия конструктора Dependency Injection? но это не дает никакого реального решения. Только сказать: «Ваш класс может делать слишком много». Я посмотрел на сервисы Facade и Aggregate, но это, казалось, просто двигало туда, где все было.

Так что мне нужна помощь в том, как сделать этот класс «менее», но при этом обеспечить тот же результат.

public AccountServices(ISomeWebServiceProvider someWebServiceProvider,
        ISomeOtherWebProvider someOtherWebProvider,
        IConfigurationSettings configurationSettings,
        IDB2Connect dB2Connect,
        IDB2SomeOtherData dB2SomeOtherData,
        IDB2DatabaseData dB2DatabaseData,
        ISharepointServiceProvider sharepointServiceProvider,
        ILoggingProvider loggingProvider,
        IAnotherProvider AnotherProvider,
        ISQLConnect SQLConnect)
    {
        _configurationSettings = configurationSettings;
        _someWebServiceProvider = someWebServiceProvider;
        _someOtherWebProvider = someOtherWebProvider;
        _dB2Connect = dB2Connect;
        _dB2SomeOtherData = dB2SomeOtherData;
        _dB2DatabaseData = dB2DatabaseData;
        _sharepointServiceProvider = sharepointServiceProvider;
        _loggingProvider = loggingProvider;
        _AnotherProvider = AnotherProvider;
        _SQLConnect = SQLConnect;
    }

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

Вот как изложен один из методов.

public ExpectedResponse GetAccountData(string AccountNumber)
    {
          // Get Needed Config Settings
          ...
          // Do some data validation before processing data
          ...
          // Try to retrieve data for DB2
          ...
          // Try to retrieve data for Sharepoint
          ...
          // Map data to response.
          ...
          // If error, handle it and write error to log
    }

Другие методы очень похожи, но они могут обращаться к SQL Server или к одной или нескольким веб-службам.

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

Спасибо

1 Ответ

1 голос
/ 25 октября 2019

Не уверен, поможет ли это, но я придумал шаблон, который я назвал GetTester, который оборачивает конструктор и немного облегчает обработку параметров. Вот пример:

 private SmartCache GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis)
 {
     memory = new Mock<IMemoryCache>();
     redis = new Mock<IRedisCache>();
     return new SmartCache(memory.Object, redis.Object);
 }

Вызывающие абоненты выглядят так, если им нужны все макеты:

 SmartCache cache = GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis);

Или вот так, если они этого не делают:

 SmartCache cache = GetTester(out _, out _);

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

...