есть современные IDE с автоматическими средствами рефакторинга. То есть если у тебя была зависимость от какого-то класса и тебе понадобилось подменить его на абстракцию - не вопрос, 5 минут и у тебя выделен интерфейс и все референсы поменялись. Для многих языков это достаточно безопасная операция которую ты просто делаешь коммитишь и готово.
2. сегодня подавляющее большинство систем делают таким образом что тебе так или иначе нужно "пересобрать все" что бы работало. Ну то есть средний проект на джавах шарпах и т.д. будет требовать рекомпиляции. Или там запакавать в новый образ докеровский. Тип в любом случае сборка деплой и тестирование подразумевают новый артефакт который ты будешь уже промоутить на прод.
Если наш контекст отличается (у нас там проект разделен на под проекты с динамической линковкой и артефакты могут независимо заменяться, пусть не в рантайме но без необходимости пересобирать все) - то там отдельный разговор и будут свои ограничения которые будут требовать больше внимания к DIP. но я хз кто сейчас так делает. Последнее что припоминаю - знакомые мобильщики так делали на больших проектах с целью распаралелить разработку. Но обычно люди которые таким занимаются уже понимают че куда зачем и как. Или как минимум "неверный выбор абстракций" начинает конфликтовать с тем ради чего динамическую линковку завозили и хоть какие-то явные сигналы что что-то не так идет.
Теперь дальше. "подменить репозиторий". В 90% ситуаций мы не будем этого делать. Ни в тестах (ибо не понятно что мы тестим) ни в чем-либо то нибыло. Да мы можем декорации какие применять, для этого нужен интерфейс но наличие интерфейса не означает соблюдение DIP (если у тебя направление зависимостей между модулями не меняется то какая разница).
Для оставшихся 10% или когда ситуация поменялась и понадобилось - выделяешь интерфейс и вперед.
Если мы говорим про то как большинство делает у тебя репозиторий это класс с 100500 методов выборок которые юзаются каждый в паре мест. И просто лепить на такое интерфейс уже как-то глупо. Да можно попытаться разбить интерфейс через ISP и тогда как бы уже интереснее и больше смысла. Но опять же для чего? В тестах подменять? Тут уже больше вариантов если разным модулям разное надо.
Если выше смотреть, "репозиторий" обычно не шарится между модулями. Тип если у тебя есть модуль какой-то подсистемы которая отвечает за свои аспекты системы, то репозиторий наружу мы никогда не будем отдавать. Вместо этого будут другие точки интеграции с другими модулями и там можно чет смотреть. Тип для организации каких-нибудь anti-corruption layers. и прочего. Обычно полезно между легаси частями приложений и новыми частями.
Потому и говорю что на примере репозитория просто не особо много пользы от DIP. Можно было бы говорить о какой-то пользе если бы у нас "репозиторий" был классический, тип просто key-value стор без выборок и прочего. Тогда это можно было бы представить какой-то более-менее понятной абстракцией которую удобно подменять и т.д. Тогда можно надежно говорить о том что мы стор базы подменили на in-memory стор и ничего не поменялось и мы все еще понимаем что тестим. Но ты много таких систем видел? Я не много.
у тебя ситуация когда репозиторий это простой интерфейс для работы с коллекцией через ключ значение и больше ничего? Никаких методов save или find some very specific things? Если так - могу представить себе где это может быть нужно. Но большинство "репозиторием" называют просто точку взаимодействия с базой, где у тебя в каком-нибудь условном OrderRepository будет жирный кусок SQL или квери билдер или LINQ с джойнами на другие сущности и прочей хуйней. И подменять такое - обычно смысла нет ибо 50% того что может потенциально сломаться это сами запросы и взаимодействие с базой. А так как "база" это обычно managed out-process dependency то есть под нашим контролем хоть и вне границ процесса - нам не обязательно это дело отделять. Мы все еще можем достаточно легко обеспечить изоляцию тест кейсов даже с реальной базой.
Бывает и то, и то. В последнее время я пришел к разделению на write model и read model. Для write это обычно getById + save + delete, а для read уже сложные выборки. Первое я подменяю кастомной полноценной реализацией
ну вот я не встречал пока в природе ситуации где такие тесты дают хоть какой-то уровень уверенности в том что система работает как надо.
Обновил исходное сообщение
а ты уверен что в этом случае ты что-то тестишь а не просто фиксируешь текущую реализацию?
На сложные запросы у меня есть тесты на сам репозиторий (реальный)
я про "моки которые ассертят что пришло в метод репозитория".
Я тестирую что код правильно составил запрос в репозиторий, и правильно использовал ответ). И этот код я пишу ещё до того как написал код реального репозитория.
а можешь псевдокодом каким показать пример такого теста?)
Сейчас у меня этими методами пользуется код, который отвечает за http api. Тест +- такой Дано: http запрос с такими-то урлой и телом Подменяем репозиторий, экспектим такие то параметры, возвращаем такой список сущностей Проверяем что http api правильно отрендерил ответ. Думал ещё это все сделать через query bus, чтобы апи не пользовался репозиторием напрямую, но пока и так норм)
ну то есть тест по факту фиксирует реализацию а не проверяет поведение
Реализацию чего мы фиксируем, не понял
Обсуждают сегодня