Pull to refresh
7
0
Send message

А насчёт given — так я вообще не про теорию, я про то как пользоваться и зачем.

Насчет английского вы правы, но однозначной русской терминологии кажется нет, т. е. ей никто особо не пользуется.


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

то, что вы описываете, это не тавтологические тесты. Тавтологические тесты — это тесты, которые повторяют имплементацию.

Дублирование же интеграционных и юнит тестов решается тем, что при наличии хороших юнит тестов интеграционные перестают быть нужны. Т.е. вопрос — почему вообще требуется запускать форму с бизнес-логикой? Что в этом целом больше, чем отдельные части?
есть и котлин и ломбок, и статический анализ, но они касаются имплементации, а тут чисто описание сценария, которое должно быть отделено от имплементации. Мой пример — невнятный в данном случае — был против недостаточного (когда тестируют только конечные проперти) и избыточного (когда тестируют все приехавшее из метода). Я подумаю, как расписать полнее.

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

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

У вас там так много аргументов, что я не знаю, какой вы имеете в виду. Я, кажется, с основными согласен. Неявные ассерты плохо, один ассерт на тест — согласен, но нерелизуемо пока, про given /when не согласен.

В данном случае копипаста показывает, что именно упало — и что мы на это рассчитывали,. Потому что если выпадет нульпойнтер до ассерта, то неясно какой именно в Order или order.getCustomer, и неясно, пропустили мы его случайно или осознанно.


Пойнт про valid order состоит в том, что надо проверять валидность ордера, а не просто нульпойнтер, коль скоро мы это задекларировали. Как именно валидность определена — это зависит от имплементации, главное не пренебрегать ассертами. Многие в погоне за покрытием ставят минимальный ассерт и бегут дальше.

В общем, я просто всегда имею в виду возможный misuse этого способа джуниорами.
Из моего опыта bulk методы сравнения часто приводят к тому, что, во-первых, объект уже перегружен свойствами, т.е. сама потребность в балк-методе — это smell.
Так же при появлении новых свойств у класса они начинают автоматически появляться в объектах и проверяться. С позиции читателя предпочтительно, чтобы тест упал и свойство было в явном виде добавлено.
Насчет коллекций труднее, там вообще трудно с ассертами, слишком много утверждений приходится подтверждать\опровергать зараз и слишком много искушений обобщить, что приводит к труднопонимаемым тестам. Я не видел какого-то реалистичного консистентного подхода пока и стараюсь избегать их. Но к мыслям по улучшению я открыт.

Не обращайте внимания, это больной)

Так это и есть самый простой способ организовать. Его легче всего читать глазами, все везде будет написано явно и мы точно знаем, если где-то что-то пропущено. Хелперы в тестах вообще раздражают. Кроме того, в реальной ситуации помимо равенства точного бывает мягкое, когда часть полей можно проигнорировать, часть contains, часть equalsIgnorcase, часть nullable, а в тесте можно точно написать, что мы ждём.

потому что в логе может быть не написано, на каком поле упало. нас же в конечном счете интересует какое конкретно свойство не соблюдено.
вопрос, не кем формализовано, а на основе чего конкретно.
одна из задач тестов и ассертов — их быстрое чтение, поэтому лучше всего сравнивать свойство за свойством в явном виде. Из CI лога сразу видно где упало и что пропущено. Всякие же быстрые способы — рекурсивные рефлексии по полям, контрольные суммы и прочее приводит к тому, что нужно перезапускать, пересматривать глазами и т.п.
На практике, когда люди пишут длинные нудные ассерты для множества свойств, они еще в процессе думают и удаляют ненужное, или находят дыры.
хм, раз уж вы упомянули YAGNI — в вашем примере не нужен ни UserRequest, ни UserProfileResponse, можно спокойно принимать айдишник в виде параметра и возвращать строку имени в качестве ответа. Т.е. несмотря на YAGNI, вы уже закладываете некоторые абстракции в предположении о будущих изменениях.

Текст этот мы читали. Аргументов в нем не очень много, и разбить их не трудно. Я, в свою очередь, вас отлично понимаю. Я сам думал как вы очень долгое время.

Логи ошибку видят. А вы логов можете не сразу увидеть. Это может быть продакшен лог и в нем может быть тысяча вызовов с 500 ошибками c NullReferenceException. Внешне это может выражаться в десятке разных багов с фронтенда, на которые тупо придется открывать тикеты, а потом накатывать хотфиксы.

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

Неважно какими средствами делать валидацию и перехват экспешенов, декларативно или императивно. В ТДД имплементация вообще дело десятое, тут главное reasoning и specification. Т.е. мы подразумеваем, что если человек написал сценарий, то он так и задумывал. А если не написал, то и не подумал. Это работает для всех.

Обсуждая детали имплементации, вы смотрите на код со стороны написавшего, а не читающего и поддерживающего. Написавшему много что может быть ясно. Вопрос, что потом с этим делать.
отличный пример!
Сначала разберем ошибочные сценарии.

1) Представим, что в вашем клиенте баг и у вас прилетает request==null, из которого возращается 500 ошибка на клиенте, причем в логах ничего нет.
2) Если request.id==null, аналогично будет возвращаться, видимо, пустой лист. (И в логах опять ничего нет, если только вы не профилируете все запросы).
3) Если id нормальный, но в базе отсутствует, что должно возращаться? То же самое, и в логах опять ничего нет.
4) Таймауты и коннекшены падают как придется

Отдельно следует заметить, что этот код не содержит _никаких_ утверждений о том, как именно он должен вести себя в ошибочных сценариях. Т.е. нет разницы между «не подумал» и «так и задумал».

Теперь предположим, что у нас есть набор изменений. Например, добавилась логика в маппинг ответа, LastName, FirstName, FullName, Middle Initial. Часть из них обязательная, часть нет, часть вычисляемая. Добавилась связанная сущность UserAddress, к которой тоже релевантны все проверки и которая может как отсутствовать в базе, так и присутствовать и быть не полной.

А если у клиента свое представление о том, как должен выглядеть payload ответа, то ему нельзя просто вернуть UserProfileResponse.

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

Т.е. у этого кода уже сейчас, в самой простой стадии, примерно нулевая устойчивость как к ошибочным сценариям, так и к сценариям самых базовых изменений и расширений.

При том, что при юнит-тестинге эта устойчивость появилась бы сразу, из коробки.

(Собственно говоря, уже написав, я сообразил, что метод возвращает лист профайлов, т.е. еще и на сценарии с множественными профайлами нужны тест-кейсы).
я думаю, что вы не правы насчет юнит-тестов, я сам до 35 лет не видел хороших в реальной жизни, а потом поменял свое мнение. Я напишу поподробнее.
Спасибо). На самом деле оценка +5 и -14, т.е. не такое уж плохое соотношение для русских форумов.

Information

Rating
Does not participate
Registered
Activity