> Не «не надо», а незачем. Их поведение (если они покрыты тестами) предсказуемо, и они чаще всего не вносят собственный эффект в тест.
Давайте немного уточним, вот есть метод, у него нет зависимостей. Теперь мы какой-то из аргументов перенесли в поле класса (сделав зависимостью). В каком случае эта зависимость будет «функциональной», а в каком — нет?
> Только вам для этого надо знать, что БД — это неявный вход для этого метода.
Как я могу этого не знать, если согласно спецификации этот метод использует мапинг из бд?
> Вот только логика там есть даже в том рудиментарном варианте, который мной написан: из реквеста достается конкретное свойство, возвращается обработанный реквест, а не исходный.
И один единственный тест эту логику проверит. Вы же выше речь вели о том, что вам надо много сетапов, т.к. разные мапинги. Но в данном случае валидность метода от того, какой мапинг лежит в бд, вообще никак не отличается, если вы и напишете кучу тестов с разными мапингами (которые будут возвращаться из моков), то если один тест прошел — остальные практически гарантированно пройдут. Зачем тогда эти тесты нужны, и почему бы не перенести их в тестирование маппера, где это будет проще (т.к. туда все что надо передается рпотсо аргументом, это всегда удобнее чем настраивать моки, согласитесь)?
> Значит, нам нужен либо (еще один) враппер вокруг IMetadataProvider.GetMappingMetadataFor
Конечно, надо — достаточно одного единственного теста, который вытягивает некоторые данные из бд и применяет некоторый маппинг. Если получилось — метод работает правильно. В итоге вам одного тестового сетапа и достаточно (о чем я выше говорил). А если вы хотите проверить именно правильность мапинга (или правильность взаимодействия с БД) — вы проверяете маппер (или провайдер), потому что это их ответственность, а не ответственность Request Map(Request).
Можно сказать тут, что любой тест для Request Map(Request) будет автоматически интеграционным, потому что сам метод единственное что делает — это обеспечивает интеграцию между двумя модулями, это метод-клей. Если вы в этом методе изолируете зависимости — то вы просто ничего не протестируете, за отсутствием какой-либо логики в данном методе.
Так мы в итоге и пришли к предлагаемому мной изначально варианту — мокать только внешние зависимости, т.к. все остальные — могут (и должны) быть функциональными.
> (ну то есть за исключением «чистых» функциональных зависимостей, конечно, но их тоже на моей памяти не мокают, ибо незачем, а значит, тоже за пределами обсуждения)
Когда я выше говорил про сервисы к БД с кучей зависимостей и методе-двустрочнике, который все что делает — это дергает пяток функций из них — то я как раз про функциональные зависимости и говорил. Оказывается, их «можно» и не мокать? Ну тогда в итоге кроме внешних сервисов и нечего мокать-то, как я и предложил изначально.
Как он может мешать, если мы говорим о внутренней реализации функции? Сигнатура у нее та же самая, данные она принимает и возвращает те же. Просто логика мапинга выделена в метод, который мы и тестируем. Тестировать же надо логику, а не БД или работу моков.
А некуда выделять. Все, что делает этот метод — дергает 5 других методов. Конечно, можно, например, разбить его так, чтобы было 2 метода по два вызова и один скомбинированный с тремя. Причем каждый из новых этих методов будет полностью бессмысленным, да и количество зависимостей тут не изменится.
> А вот как мне сделать, чтобы SUT использовал зависимости, которые используют другие зависимости, которые где-то потом неизвестно где используют эти моки?
Эм… Вам надо конфигурировать только те, последние моки. Точно так же, как вы все моки конфигурируете.
> а метаинформацию о том, как именно делается маппинг, берет из хранилища.
Ну вот у вас и нарушение SRP — вы в одном методе и в хранилище лезете, и какую-то логику мапинга реализуете. Кто еще этот мапинг разбирает и смотрит, как по нему, с-но, мапить?
> Еще раз, по буквам. В каждом тесте я объявляю тот объект, который ему нужен. Один объект на тест. Суммарно k объектов. Я не объявляю все остальные объекты они мне не интересны.
Давайте подведем некоторый итог:
1. В вашем коде неким образом получается избегать большого количества зависимостей, не нарушая SRP, в моем — нет (например — у вас бд, бизнес-логика содержится в сервисах, их много, они разделены согласно доменной логике, какой-нибудь метод-двустрочник может использовать полдесятка разных зависимостей, как вы тут будете количество зависимостей снижать?)
2. У вас есть всякий indirect output, я его по возможности избегаю и стараюсь тестировать при помощи черного ящика.
Возможно, в _вашей_ ситуации юнит-тесты имеют смысл, в моей — очевидно, не совсем так.
> Читать «фреймворки для моков редко показывают примеры глобальных повторно используемых сетапов».
Я не могу понять, чем,, по-вашему, настройка глобального сетапа отличается от любого другого. Почему вы решили, что там есть какие-то особенности? Вызываются те же методы, с тем же результатом.
Давайте немного уточним, вот есть метод, у него нет зависимостей. Теперь мы какой-то из аргументов перенесли в поле класса (сделав зависимостью). В каком случае эта зависимость будет «функциональной», а в каком — нет?
Как я могу этого не знать, если согласно спецификации этот метод использует мапинг из бд?
> Вот только логика там есть даже в том рудиментарном варианте, который мной написан: из реквеста достается конкретное свойство, возвращается обработанный реквест, а не исходный.
И один единственный тест эту логику проверит. Вы же выше речь вели о том, что вам надо много сетапов, т.к. разные мапинги. Но в данном случае валидность метода от того, какой мапинг лежит в бд, вообще никак не отличается, если вы и напишете кучу тестов с разными мапингами (которые будут возвращаться из моков), то если один тест прошел — остальные практически гарантированно пройдут. Зачем тогда эти тесты нужны, и почему бы не перенести их в тестирование маппера, где это будет проще (т.к. туда все что надо передается рпотсо аргументом, это всегда удобнее чем настраивать моки, согласитесь)?
> Значит, нам нужен либо (еще один) враппер вокруг IMetadataProvider.GetMappingMetadataFor
Вроде, вы сами ответили на свой вопрос?
Так это уже вопрос исключительно вашей архитектуры. Никто же вам не мешает максимально выделять логику в чистые функции.
Кроме того, не совсем понятно, почему чистые зависимости мокать не надо, а «грязные» — надо. Почему такое разделение?
Можно сказать тут, что любой тест для Request Map(Request) будет автоматически интеграционным, потому что сам метод единственное что делает — это обеспечивает интеграцию между двумя модулями, это метод-клей. Если вы в этом методе изолируете зависимости — то вы просто ничего не протестируете, за отсутствием какой-либо логики в данном методе.
Когда я выше говорил про сервисы к БД с кучей зависимостей и методе-двустрочнике, который все что делает — это дергает пяток функций из них — то я как раз про функциональные зависимости и говорил. Оказывается, их «можно» и не мокать? Ну тогда в итоге кроме внешних сервисов и нечего мокать-то, как я и предложил изначально.
Как он может мешать, если мы говорим о внутренней реализации функции? Сигнатура у нее та же самая, данные она принимает и возвращает те же. Просто логика мапинга выделена в метод, который мы и тестируем. Тестировать же надо логику, а не БД или работу моков.
Ну а как вы это делаете обычно? Через IoC-контейнер, я полагаю. Зарегистрировать/заменить моки для теста в IoC-контейнере вы можете откуда угодно.
С моей точки зрения — нарушение. У вас в одном методе смешана логика и доступ к хранилищу.
Если по смыслу они должны быть снаружи — то кто вам мешает их вынести?
Это как может быть? Один метод достает мапинг из хранилища и возвращает, второй — принимает то, что вернул предыдущий, и применяет.
Да, для вполне конкретной и определенной задачи.
> Координаторов в системе мало.
Ну вот в моей практике все строго наоборот.
А некуда выделять. Все, что делает этот метод — дергает 5 других методов. Конечно, можно, например, разбить его так, чтобы было 2 метода по два вызова и один скомбинированный с тремя. Причем каждый из новых этих методов будет полностью бессмысленным, да и количество зависимостей тут не изменится.
Я буду передавать эти мапинги аргументом в данном случае.
Эм… Вам надо конфигурировать только те, последние моки. Точно так же, как вы все моки конфигурируете.
Ну вот у вас и нарушение SRP — вы в одном методе и в хранилище лезете, и какую-то логику мапинга реализуете. Кто еще этот мапинг разбирает и смотрит, как по нему, с-но, мапить?
Давайте подведем некоторый итог:
1. В вашем коде неким образом получается избегать большого количества зависимостей, не нарушая SRP, в моем — нет (например — у вас бд, бизнес-логика содержится в сервисах, их много, они разделены согласно доменной логике, какой-нибудь метод-двустрочник может использовать полдесятка разных зависимостей, как вы тут будете количество зависимостей снижать?)
2. У вас есть всякий indirect output, я его по возможности избегаю и стараюсь тестировать при помощи черного ящика.
Возможно, в _вашей_ ситуации юнит-тесты имеют смысл, в моей — очевидно, не совсем так.
Я не могу понять, чем,, по-вашему, настройка глобального сетапа отличается от любого другого. Почему вы решили, что там есть какие-то особенности? Вызываются те же методы, с тем же результатом.