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

Сообщение Re[5]: Что такое Dependency Rejection от 12.10.2023 8:55

Изменено 12.10.2023 10:48 Pauel

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

P>>Сейчас это называется плохо протестировать — вместо простых и дешовых юнит-тестов вам надо обмазываться моками, и писать хрупкие тесты вида "вызвали функцию репозитория"

P>>То есть, это тесты "как написано", тавтологические
·>Технически то же самое.

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

> Вместо передачи параметров проверки результатов пюрешки (pure function), прогоняются данные через моки.


И это проблема, т.к. моки так или иначе искажают действительность. Т.к. всегда есть риск, что с моками работает, а без моков — ни гу-гу. При росте сложности приложения эти риски начинают срабатывать регулярно.
Например, научить новичка внятно пользоваться моками да правильно покрывать тесты используя такой подход довольно трудно, на это уходят годы. А потому рано или поздно будете ловить последствия в проде.

> Тестируется ровно то же, ровно так же. Разница же лишь семантическая


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

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

·>Создание на основе именно прочитанных данных, а не чего попало. Чтение данных согласуется с созданием.

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

P>>Вот-вот, именно это и есть тренд последних десяти лет, при чем не только в функциональных языках.

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

Именно! А вы предлагаете грязный ооп использовать на уровне методов технической реализации. Потому вам и нужны моки. ООП это парадигма для управления сложностью взаимодействия. ФП — для управления сложностью вычислений. Отсюда ясно, что нам надо и то, и другое — склеить взаимодействие с вычислениями. Создание — тупо вычисления, а вы сюда втискиваете зависимость на БД и вещаете что это хорошо.
Опомнитесь — ваш подход из 80х-90х, когда TurboVision в MSDOS встречали на ура.

·>Покажи мне этот однострочный интеграционный тест для tryAcceptComposition. Авторы статьи — "забыли".


expect(service.arrange({sits: goodValue})).to.match({succeed: {id: notEmpty(string())}});


·>Пока итог такой. Был грязный метод tryAccept на 4 строки, который требует только один юнит-тест, хотя с моком.


Неверно — была монолитная иерархия вызовов во главе с tryAccept который только сам по себе 4 строки.
И тестировать нужно всю эту иерархию, для чего вам и нужны моки.

·>Они это преобразовали в пюрешный tryAccept на 3 строки, который вроде как легко покрывается юнит-тестом, но без мока. Ура.


Именно!

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


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

P>>Ничего не крошится. все функции будут сделаны по одной и той же схеме.

·>Как эти разрозненные функции будут объединяться в единую определяемую бизнесом сущность MaitreD?

MaitreD это метафора уровня реализации, никакой бизнес это не определяет. Бизнесу надо что бы работали юз-кейсы вида "зарезервировать столик на 10 мест на 31е декабря между баром и сценой". Используете вы MaitreD или нет, дело десятое.
На основе коротких функций мы можем менять компоновку уже по ходу пьесы подстраиваясь под изменения треботваний, а не бетонировать код на все времена.

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

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

Похоже, вы просто не в курсе, как подобное реализуется в функциональном стиле, а потому порете чушь. Нам нужен аудит не вызова tryAccept, а аудит всей транзации которая изначально будет грязной, принципиально. Т.е. вещи вида "третьего дня разместили заявку xxx(детали) ... статус успешно, детали(...)"
То есть, к известным эффектам мы всего то добавляем еще один. И емейл это ровно такой же эффект. Т.е. нам нужна всего то парочка мидлвар, которые и берут на себя все что надо.
Более того, и запись в очередь это снова такой же эффект

Вот вам ваши и аудит, и эвент, еще и валидация.
const Schema = {
    arrange: ...,
    arranged: ...,
}

@useAudit(Schema.arrange, Schema.arranged)
@useEvent(Schema.arranged)
@validate(Schema.arrange)
@presenter(Schema.arranged)
arrange(request: typeof Schema.arrange): Result<[Error, typeof Schema.arranged]> {
...
}
Re[5]: Что такое Dependency Rejection
Здравствуйте, ·, Вы писали:

P>>Сейчас это называется плохо протестировать — вместо простых и дешовых юнит-тестов вам надо обмазываться моками, и писать хрупкие тесты вида "вызвали функцию репозитория"

P>>То есть, это тесты "как написано", тавтологические
·>Технически то же самое.

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

> Вместо передачи параметров проверки результатов пюрешки (pure function), прогоняются данные через моки.


И это проблема, т.к. моки так или иначе искажают действительность. Т.к. всегда есть риск, что с моками работает, а без моков — ни гу-гу. При росте сложности приложения эти риски начинают срабатывать регулярно.
Например, научить новичка внятно пользоваться моками да правильно покрывать тесты используя такой подход довольно трудно, на это уходят годы. А потому рано или поздно будете ловить последствия в проде.

> Тестируется ровно то же, ровно так же. Разница же лишь семантическая


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

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

·>Создание на основе именно прочитанных данных, а не чего попало. Чтение данных согласуется с созданием.

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

P>>Вот-вот, именно это и есть тренд последних десяти лет, при чем не только в функциональных языках.

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

Именно! А вы предлагаете грязный ооп использовать на уровне методов технической реализации. Потому вам и нужны моки. ООП это парадигма для управления сложностью взаимодействия. ФП — для управления сложностью вычислений. Отсюда ясно, что нам надо и то, и другое — склеить взаимодействие с вычислениями. Создание — тупо вычисления, а вы сюда втискиваете зависимость на БД и вещаете что это хорошо.
Опомнитесь — ваш подход из 80х-90х, когда TurboVision в MSDOS встречали на ура.

·>Покажи мне этот однострочный интеграционный тест для tryAcceptComposition. Авторы статьи — "забыли".


expect(service.arrange({sits: goodValue})).to.match({succeed: {id: notEmpty(string())}});


·>Пока итог такой. Был грязный метод tryAccept на 4 строки, который требует только один юнит-тест, хотя с моком.


Неверно — была монолитная иерархия вызовов во главе с tryAccept который только сам по себе 4 строки.
И тестировать нужно всю эту иерархию, для чего вам и нужны моки.

·>Они это преобразовали в пюрешный tryAccept на 3 строки, который вроде как легко покрывается юнит-тестом, но без мока. Ура.


Именно!

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


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

P>>Ничего не крошится. все функции будут сделаны по одной и той же схеме.

·>Как эти разрозненные функции будут объединяться в единую определяемую бизнесом сущность MaitreD?

MaitreD это метафора уровня реализации, никакой бизнес это не определяет. Бизнесу надо что бы работали юз-кейсы вида "зарезервировать столик на 10 мест на 31е декабря между баром и сценой". Используете вы MaitreD или нет, дело десятое.
На основе коротких функций мы можем менять компоновку уже по ходу пьесы подстраиваясь под изменения треботваний, а не бетонировать код на все времена.

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

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

Похоже, вы просто не в курсе, как подобное реализуется в функциональном стиле, а потому порете чушь. Нам нужен аудит не вызова tryAccept, а аудит всей транзации которая изначально будет грязной, принципиально. Т.е. вещи вида "третьего дня разместили заявку xxx(детали) ... статус успешно, детали(...)"
То есть, к известным эффектам мы всего то добавляем еще один. И емейл это ровно такой же эффект. Т.е. нам нужна всего то парочка мидлвар, которые и берут на себя все что надо.
Более того, и запись в очередь это снова такой же эффект

Вот вам ваши и аудит, и эвент, еще и валидация.
const Schema = {
    arrange: ...,
    arrangeResult: ...,
}


@validate(Schema.arrange)
@useAudit(Schema.arrange, Schema.arranged)
@useEvent(Schema.arrangeResult)
@presenter(Schema.arrangeResult)
arrange(request: typeof Schema.arrange): Result<[Error, typeof Schema.arrangeResult]> {
...
}