Информация об изменениях

Сообщение Re[4]: Что такое Dependency Rejection от 11.10.2023 10:44

Изменено 11.10.2023 13:58 ·

Re[4]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:

P>·>Что-то я не понял прикол. Обычный "грязный" код MaitreD в начале, хоть был и грязным, но его можно было хорошо протестировать и бизнес-логика была сосредоточена в одном месте — прочитали список и репозитория, выполнили вычисления, проверили условия, сохранили результат, дали ответ — это всё пишется как слышится по бизнес-требованиям

P>·> Соответствующий тест будет тестировать именно бизнес-логику и тест написать легко — запихиваем мок репозитория и ассертим что репозиторий используется ожидаемым образом в разных сценариях.
P>Сейчас это называется плохо протестировать — вместо простых и дешовых юнит-тестов вам надо обмазываться моками, и писать хрупкие тесты вида "вызвали функцию репозитория"
P>То есть, это тесты "как написано", тавтологические
Технически то же самое. Вместо передачи параметров проверки результатов пюрешки (pure function), прогоняются данные через моки. Тестируется ровно то же, ровно так же. Разница же лишь семантическая — в случае пюрешки у тебя некий абстрактный кусок кода, который как-то преобразует данные которые неясно откуда берутся и куда деваются. В случае моков — готовый к использованию неделимый юнит, один-к-одному отражающий бизнес-требования. В бизнес-требованиях никакого пюре нет, там именно грязная модификация реального мира.

P>·>А "образцово-показательный" код в конце статьи разрывает бизнес-логику посередине — чтение и создание.

P>Из бизнес логики здесь только создание. Вот это и тестируем юнит-тестами безо всяких моков. Что там будет в чтении-дело — дело десятое. Отделили мух от котлет.
Создание на основе именно прочитанных данных, а не чего попало. Чтение данных согласуется с созданием.

>> Выносит чистую функцию (которая настолько проста, что там даже тестировать-то нечего)

P>Вот-вот, именно это и есть тренд последних десяти лет, при чем не только в функциональных языках.
Тренд в объединении подходов: глобально — грязный ооп (т.е. на уровне больших частей системы) бизнеса, локально — функциональное пюре (на уровне методов) технической реализации.

>> и код работы с базой в tryAcceptComposition — который тестами уже никак нормально не покрывается, т.к. там connectionString и глобальный DB. И даже теряется тот факт, что чтение и запись данных происходит из того же репозитория.

P>Это интеграционный код, он покрывается интеграционным тестом. не надо мокать базу, что бы убедиться что туда приходит коннекшн стринг. однострочный интеграционный тест даст нам куда более сильную гарантию
Покажи мне этот однострочный интеграционный тест для tryAcceptComposition. Авторы статьи — "забыли".

Пока итог такой. Был грязный метод tryAccept на 4 строки, который требует только один юнит-тест, хотя с моком.
Они это преобразовали в пюрешний tryaccept на 3 строки, который вроде как легко покрывается юнит-тестом, но без мока. Ура. Но замели сложность в ещё один tryAcceptComposition на 3 строки (притом довольно хитрый! со всякими магическими заклинаниями liftIO, $, return, flip, .), для которого "забыли" написать интеграционный тест. Почему это проще — я ну никак понять не могу.

P>·>Ещё "ух! Мы заменили целый класс MaitreD на всего одну функцию tryAccept" — тоже какая-то странная риторика. В реальности MaitreD будет иметь ещё cancelReservation/reschedule/etc и опять же соответствовать вполне реальной бизнес-сущности. Как логически объединять пачку разрозненных функций — тоже неясно.

P>·>И если бизнес-логика обрастёт подробностями (например, добавить аудит, посылку емейла о результате, и т.п.) то вообще вся логика раскрошится в пыль и понять что к чему, протетсировать всё как целое — станет совсем сложно.
P>Ничего не крошится. все функции будут сделаны по одной и той же схеме.
Как эти разрозненные функции будут объединяться в единую определяемую бизнесом сущность MaitreD?

P>Аудит, емейл о результате — какую вы здесь проблему видите?

В том, что результатом tryAccept может быть пачка несвязных результатов, которые должны быть распределены соответсвующим образом: результат резервации — клиенту, аудит — сервису аудита, емейл — smtp-серверу и т.п. Если это переделывать в пюре, придётся конструировать какие-то сложные результирующие объекты, которые потом надо будет ещё потом правильно разбирать на части. Или как ты это видишь?
Re[4]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:

P>·>Что-то я не понял прикол. Обычный "грязный" код MaitreD в начале, хоть был и грязным, но его можно было хорошо протестировать и бизнес-логика была сосредоточена в одном месте — прочитали список и репозитория, выполнили вычисления, проверили условия, сохранили результат, дали ответ — это всё пишется как слышится по бизнес-требованиям

P>·> Соответствующий тест будет тестировать именно бизнес-логику и тест написать легко — запихиваем мок репозитория и ассертим что репозиторий используется ожидаемым образом в разных сценариях.
P>Сейчас это называется плохо протестировать — вместо простых и дешовых юнит-тестов вам надо обмазываться моками, и писать хрупкие тесты вида "вызвали функцию репозитория"
P>То есть, это тесты "как написано", тавтологические
Технически то же самое. Вместо передачи параметров проверки результатов пюрешки (pure function), прогоняются данные через моки. Тестируется ровно то же, ровно так же. Разница же лишь семантическая — в случае пюрешки у тебя некий абстрактный кусок кода, который как-то преобразует данные которые неясно откуда берутся и куда деваются. В случае моков — готовый к использованию неделимый юнит, один-к-одному отражающий бизнес-требования. В бизнес-требованиях никакого пюре нет, там именно грязная модификация реального мира.

P>·>А "образцово-показательный" код в конце статьи разрывает бизнес-логику посередине — чтение и создание.

P>Из бизнес логики здесь только создание. Вот это и тестируем юнит-тестами безо всяких моков. Что там будет в чтении-дело — дело десятое. Отделили мух от котлет.
Создание на основе именно прочитанных данных, а не чего попало. Чтение данных согласуется с созданием.

>> Выносит чистую функцию (которая настолько проста, что там даже тестировать-то нечего)

P>Вот-вот, именно это и есть тренд последних десяти лет, при чем не только в функциональных языках.
Тренд в объединении подходов: глобально — грязный ооп (т.е. на уровне больших частей системы) бизнеса, локально — функциональное пюре (на уровне методов) технической реализации.

>> и код работы с базой в tryAcceptComposition — который тестами уже никак нормально не покрывается, т.к. там connectionString и глобальный DB. И даже теряется тот факт, что чтение и запись данных происходит из того же репозитория.

P>Это интеграционный код, он покрывается интеграционным тестом. не надо мокать базу, что бы убедиться что туда приходит коннекшн стринг. однострочный интеграционный тест даст нам куда более сильную гарантию
Покажи мне этот однострочный интеграционный тест для tryAcceptComposition. Авторы статьи — "забыли".

Пока итог такой. Был грязный метод tryAccept на 4 строки, который требует только один юнит-тест, хотя с моком.
Они это преобразовали в пюрешный tryAccept на 3 строки, который вроде как легко покрывается юнит-тестом, но без мока. Ура. Но замели сложность в ещё один tryAcceptComposition на 3 строки (притом довольно хитрый! со всякими магическими заклинаниями liftIO, $, return, flip, .), для которого теперь требуется писать ещё и интеграционный тест. Удвоили кол-во кода, усложнили тестирование... а почему это проще — я ну никак понять не могу.

P>·>Ещё "ух! Мы заменили целый класс MaitreD на всего одну функцию tryAccept" — тоже какая-то странная риторика. В реальности MaitreD будет иметь ещё cancelReservation/reschedule/etc и опять же соответствовать вполне реальной бизнес-сущности. Как логически объединять пачку разрозненных функций — тоже неясно.

P>·>И если бизнес-логика обрастёт подробностями (например, добавить аудит, посылку емейла о результате, и т.п.) то вообще вся логика раскрошится в пыль и понять что к чему, протетсировать всё как целое — станет совсем сложно.
P>Ничего не крошится. все функции будут сделаны по одной и той же схеме.
Как эти разрозненные функции будут объединяться в единую определяемую бизнесом сущность MaitreD?

P>Аудит, емейл о результате — какую вы здесь проблему видите?

В том, что результатом tryAccept может быть пачка несвязных результатов, которые должны быть распределены соответсвующим образом: результат резервации — клиенту, аудит — сервису аудита, емейл — smtp-серверу и т.п. Если это переделывать в пюре, придётся конструировать какие-то сложные результирующие объекты, которые потом надо будет ещё потом правильно разбирать на части. Или как ты это видишь?