Как разрешить циклические зависимости для проектов, когда вы хотите удалить дублированный код?

Представьте, что у вас есть проекты:

  • Project testUtils, который содержит несколько тестовых помощников для ваших проектов, все проекты зависят от него.
  • Мобильный проект, который содержит MobileService и MobileController, который зависит от testUtils
  • Проект 'customer', который содержит CustomerService и CustomerController, который зависит от мобильного телефона, а также testUtils

введите описание изображения здесь

Вы создаете MobileServiceDouble, реализующий IMobileService в своем мобильном проекте, который проверяет зависимости при тестировании вашего MobileController.

Также вы хотите создать несколько тестов для вашего CustomerController. Этот класс зависит от MobileController. Вы решили не издеваться над MobileController. Вместо этого вы издеваетесь над зависимостями MobileController: MobileService. Вы создаете второй MobileServiceDouble, реализующий IMobileService.

Ваши тесты работают нормально, и вы решаете провести рефакторинг дублированного кода. Так, каков хороший способ устранить ваши дубликаты?

Вы не можете поместить MobileServiceDouble в свои testutils, потому что в противном случае у вас была бы круговая зависимость: mobile <-> testutils Ваш MobileServiceDouble реализует IMobileService, который находится в мобильном телефоне.

введите описание изображения здесь

Вы можете переместить интерфейс для вашего MobileService. Но вы не хотите иметь какие-либо производственные интерфейсы в своем проекте testutil.

введите описание изображения здесь

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

Что ты предлагаешь?

Обновление 1: Я немного изменяю зависимости, чтобы указать, что это не проблема, потому что я решил использовать настоящий MobileController в своих тестах:

Представьте, что есть 3 пакета:

  • Проект СервисРегистрация
  • Project Mobile зависит от ServiceRegistry и TestUtils
  • Заказчик проекта зависит от ServiceRegistry и TestUtils
  • Project TestUtils

Mobile и Customer - это проекты, в которых есть модульные тесты. Таким образом, оба хотят макетировать, например, класс ServiceRegistryManager проекта ServiceRegistry.

введите описание изображения здесь

В проектах Customer and Mobile создается ServiceRegistryManagerFakes. Тот же сценарий: как реорганизовать это дублирование? Я перемещаю поддельный класс в TestUtils.

введите описание изображения здесь

Результатом является круговая зависимость. Пока я пишу, я думаю о проекте FakeTest, который может решить мою проблему:

введите описание изображения здесь

На данный момент мои зависимости и мои проекты увеличились. Это кажется очень хрупким, потому что если моему ServiceRegistry нужен класс Fake, мне нужно создать еще один проект testFake2:

введите описание изображения здесь

Если зависимость моего нового Project TestFakes2 добавляет зависимость к классу, который зависит от моей ServiceRegistry, тогда я снова столкнусь с проблемами.

введите описание изображения здесь

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

Ищете больше идей:).

1 ответ

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

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

  2. Также вы хотите создать несколько тестов для вашего CustomerController. Этот класс зависит от MobileController. Вы решили не издеваться над MobileController. Вместо этого вы издеваетесь над зависимостями MobileController: MobileService.

    Почему вы не хотите издеваться над MobileController? Ваш CustomerController следует использовать двойной тест MobileController, Не следует делать предположение, что MobileController использования MobileService, Это вводит связь между CustomerController Тест и мобильный сервис. Так что если однажды вы измените реализацию MobileController (чтобы он больше не использовал MobileService) без изменения контракта этого класса, тесты в другом модуле (customer) начнет проваливаться.

  3. У ваших классов есть четко определенный контракт? Соответствуют ли они шаблону единой ответственности?

  4. Обеспечиваете ли вы разделение интересов? Другими словами, есть ли у вас четкие правила, какая логика должна быть внутри контроллеров, а какая внутри сервисов? Если так, зачем вам использовать контроллер от другого контроллера?

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

  6. Наконец, вы используете инъекцию зависимости?

Другие вопросы по тегам