Re[12]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 03.12.23 19:23
Оценка:
Здравствуйте, Pauel, Вы писали:

P>>>"деталь реализации" означает что разница в свойствах несущественна и инструменты взаимозаменяемы, т.к. эквивалентны

P>·>Да. Верно. Вместо "дёрнуть метод" — "вернуть данные, которые будут переданы методу"
P>Это и есть проблема. В новой версии кода например некому будет возвращать, и некому передавать, при полном соответствии с требованиям.
P>А вот тесты стали красными. Гы-гы.
P>И если код меняется часто, в силу изменения требований, вам надо каждый раз строчить все тесты с нуля.
P>Вот пример — у нас есть требование вернуть hash токен что бы по контенту можно было находить метадату итд
P>кода получается вагон и маленькая тележка
Лучше код покажи. Так что-то не очень ясно.

P>А теперь фиаско — в либе есть уязвимость и проблема с перформансом, и её надо заменить, гы-гы все тесты на моках ушли на помойкку

Круто же — все тесты кода, который используют уязвимую тормозную либу — стали красными. По-моему, успех.

P>А если делать иначе, то юнит тестом покрываем вычисления токена — передаём контент и для него получаем тот самый токен.

P>Теперь можно либу менять хоть по сто раз на дню, тесты это никак не меняет
Иными словами ты просто предлагаешь заворачивать стороннюю либу в свою обёртку. Это хорошо, иногда. Вот только моки-то тут причём?

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

Ага. Вот только эта твоя обёртка как будет попадать в использующий код? Через зависимости. И будешь мокать эту обёртку.

P>>>То есть, это инструменты которые решают разные классы задач — вместо "assert x = y" тестируем "унутре вызвали вон тот метод с такими параметрами"

P>·>"verify(mock).x(y)"
P>Вместо просто assert x = y вам надо засетапить мок и надеяться, что новые требования не сломают дизайн вашего решения.
А чего там сетапить-то? var mock = Mockito.mock(Thing.class). А что там может ломаться и зачем?

P>>>Собственно, уже 20 лет как известно, что особенностью тестов на моках является их хрупкость, т.к. привязываемся буквально к "как написано"

P>·>Это твои заморочки.
P>Вы чуть ниже сами об этом же пишете. Тесты на моках это те самые вайт бокс тесты, принципиально. И у них известный набор проблем, среди которых основной это хрупкость.
Моки и вайтбокс — это ортогональные понятия.

P>·>Я не понимаю какие проблемы у тебя с моками. А ты хранишь это в секрете. Только общие слова-отмазы "всем давно известно".

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

P>·>И ты тут телегу впереди лошади поставил. Ну да, у нас есть дизайн с side effects — что ж, либо давайте всё переписывать на хаскель, либо просто заюзаем моки для тестирования. У тебя есть третий вариант?

P>При чем здесь сайд эффекты?
Условно говоря... Моки используются чтобы чего-то передать или проверить чего нет в сигнатуре метода. Т.е. метод с сайд-эффектами — его поведение зависит не только от его параметров и|или его результат влияет не только на возвращаемое значение.

P>>>·>Ты так и не показал как ты предлагаешь решать эту проблему, только заявил, что решается одной строчкой, но не показал как. Проигнорировал вопрос дважды: Напиши однострочный интеграционный тест для tryAcceptComposition.

P>>>Однострочный интеграционный код для tryAcceptCompositio это просто вызов самого контроллера верхнего уровня.
P>·>Проигнорировал вопрос трижды: Напиши однострочный интеграционный тест для tryAcceptComposition. Код в студию!
P>
P>expect(new class().tryTryAcceptComposition(parameter)).to.eq(value);
P>

Заходим на четвёртый круг. Я не знаю что это такое и к чему относится. Напомню код:
tryAcceptComposition :: Reservation -> IO (Maybe Int)
tryAcceptComposition reservation = runMaybeT $ 
  liftIO (DB.readReservations connectionString $ date reservation)
  >>= MaybeT . return . flip (tryAccept 10) reservation
  >>= liftIO . DB.createReservation connectionString


P>>>Так уже. Это ж вы почемуто считаете что моки делают иерархию плоской, и аргументов не приводите.

P>·>Где я так считаю? Моки ничего сами не делают. Это инструмент тестирования.
P>Вот некто с вашего акканута пишет
P>

P>Ну так внедрение зависимостей DI+CI это и есть плоская иерархия.

Выделил твою цитату выше. И где ты у меня тут слово "мок" нашел?

P>>>Это принцип direct mapping, к ооп он имеет опосредованное отношение. Как вы будете сами сущности моделировать — дело десятое. Городить классы сущностей — нужно обоснование

P>·>Ок. Могу согласится, что это дело вкуса. Не так важно класс это или лямбда. Суть моего вопроса по статье — как же писать тесты, чтобы не оставалось непокрытого кода.
P>Нету такой задачи покрывать код. Тестируются ожидания пользователя, соответствие требованиями, выявление особенностей поведения и тд.
Покрытие позволяет найти непротестированный код. Если мы его нашли — мы видим пропущенные требования и начинаем выяснять ожидание пользователя в пропущенном сценарии. И вот после этого тестируем ожидание.

P>>>Какой именно код не протестирован?

P>·>функция tryAcceptComposition. Ты обещал привести однострочный тест, но до сих пор не смог.
P>Мне непонято что в моем объяснении вам не ясно
Как выглядит код, который тестирует tryAcceptComposition.

P>>>·>Ну так внедрение зависимостей DI+CI это и есть плоская иерархия. В конструкторе все зависимости, методы — целевая бизнес-логика. В статье это и было показано, что такой подход эквивалентен частичному применению функций — код под капотом идентичный получается.

P>>>Вам для моков нужно реализовывать в т.ч. неявные зависимости. Если вы делаете моками плоску структуру, получаются тесты "проверим что вызвали вон тот метод" т.е. "как написано"
P>·>Это называется whitebox тестирование. Причём тут моки?
P>Браво — вы вспомнили про вайтбокс. Моки это в чистом виде вайтбокс, я это вам в прошлый раз говорил раз десять
P>А вот если вы идете методом блакбокс тестирования, то никакие моки вам применить не удастся, и это дает более устойчивый код решения и более устойчивые тесты
Вайтбокс знает имплементацию. Каким образом ты будешь блакбоксить такое? int square(int x) {return x == 47919 ? 1254 : x * x;}. Заметь, никаки моков, тривиальная пюрешка.

P>>>А если мокаете клиент БД или саму бд, то вам надо под тест сконструировать пол-приложения.

P>·>Не очень понял что именно ты имеешь в виду. Вот мы тут вроде статью обсуждаем, там есть код, вот покажи на том коде что не так и как сделать так.
P>Уже объяснил. Что именно вам непонятно?
А я просил что-то объяснять? Я просил показать код.

P>·>А конкретно? С примерами кода. Как эти ожидания выражаются? Почему "вернуть X" — ты называешь "ожидание пользователя", а "вызвать x()" — "протокол взаимодействия"? И что всё эти термины значат? В чём принципиальная разница?

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

P>>>Успокойтесь. Добавлять классы на основании разговора на митинге — дело так себе. Актор может вообще оказаться целой подсистемой, а может и наколеночным воркером, или даже лямда-функцией

P>·>Ок. Каким образом ты делаешь соответствие термина в FRD с куском кода?
P>Выбираю абстракцию наименьшего размера, которая покрывает кейс, и которую я могу протестировать
А если её потом надо будет переделать на другой размер?

P>>>У вас видение из 90х или 00х, когда аннотациями лепили абы что.

P>·>А что сейчас? Сделай show&tell, или хотя бы ссылку на.
P>Я уже показывал что в прошлый раз, что в этот — отлистайте да посмотрите. Это ваша проблема что вы умеете только свойства инжектать да конструкторы лепить
Ты просто всё ещё не понял: я умею не лепить аннотации.

P>>>Ровно наоборот — аннотации в покрытии учавствуют, просто обязаны.

P>·>Покажи мне coverage report, где непокрытые аннотации красненьким подсвечены.
P>
P>Expectation failed: ControllerX tryAccept to have metadata {...}
P>

Это не coverage report, это test report.

P>>>Прикрутили метаданные — есть тест на это. Используются ли эти метаданные — тоже есть тест.

P>·>Покажи как выглядит тест на то, что аннотация повешена корректно.
P>
P>const tryAccept = Metadata.from(ControllerX, 'tryAccept')
P>expext(tryAccept).metadata.to.match({...})
P>

Самое что ни на есть вайтбокс тестирование. Что конкретный метод содержит конкретную аннотацию. В случае моков это можно изоморфно написать как-то так:
controllerX.tryAccept(...)
verify(metadata).to.match(...);

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