Pull to refresh

Comments 53

лень — двигатель прогресса. давно доказано
При этом вот в чём будет выражаться лень:

Вы забыли варианты «лень написать тест еще на вот такой случай» и «лень думать, какие случаи бывают».
Наверное, последний пункт — самый проблематичный в TDD.
От него спасает «попросить тестировщиков написать тест-кейсы» и «просмотреть code coverage». Но все равно не полностью.

Поэтому no silver bullet here too.
Небольшой лирический отступ: Программист всегда может логически обосновать свою лень! :)
Лаконичный пример:
Иногда лучше остаться спать дома в понедельник, чем провести всю неделю отлаживая написанный в понедельник код.
— Christopher Thompson
«Самую сложную задачу я поручу решать самому ленивому программисту» (с) Билл Гейтс.
UFO just landed and posted this here
И он потом меня сможет уговорить, что на самом деле задачу можно и не решать/задача не актуальна
47 человек поленились оставить тут комментарий.
Поставил бы плюс уже за один только заголовок статьи :-)
Но лень было продолжать, почему всё же не поставил...)
Тесты, вообще, вселяют спокойствие и уменьшают тревожность при написании следующего участка кода, когда более-менее уверен, что прошлый участок работает, а если перестанет работать, то это всплывёт.
Странно, что еще никто не привел цитату Билла Гейтса… Наверное всем лень.

Ах нет же, мне просто было лень читать комментарии…
Цитаты из Билли Гейтса приводят, когда лень разыскивать более подходящие к случаю цитаты.
>Тест обычно не очень большой, очень легко его скопировать\вставить и, заменив пару символов, превратить в похожий, но чуть-чуть другой.

И получить тонны неподдерживаемого кода…
И получить тонны кривых тестов (копипаста она такая)…
Почему то все думают что тесты решают все проблемы. Но в реальности плохо написанный тест создаст еще больше проблем чем если бы его не было вовсе. Поэтому тесты требу.т не меньшего внимания и серйозности подхода как напиание самого продакшен кода. В итоге нормальный тест занимает раз в 5 больше времени чем написание самого функционала. А это говорит о том что такие пряники досутпны далеко не каждому проекту.
Да не решают они всех проблем, конечно. Почему бы не начать с написания простых тестов? А 80% от всех тестов действительно написать легко. Да, оставшиеся 20 часто требуют мощных фреймворков, написания моков и т.д. Но даже если написать только простые тесты — это уже в 100 раз лучше, чем никаких. Лично я тесты, которые надо писать в 5 раз дольше самого функционала не пишу вообще.
Из личного опыта — сложный тест пишется один раз. Зато потом он может несколько раз сэкономить кучу времени при отлове трудообнаружимого при обычных условиях (всмысле, без этого теста) бага.
Лично я тесты, которые надо писать в 5 раз дольше самого функционала не пишу вообще.

ибо лень?
Не окупается потому что. Если тест тестирует какую-то ну очень хитро-через-пень-колоду-сделанную хрень, то высока вероятность, что она вообще пропадёт\переделается нормально\разделится на несколько.
Ну так для такой хитрой хрени и придумали интеграционное тестирование. Которое кстати выловит косяки рефакторинга хитрой хрени. А переделывать/добавлять нужно будет разве что юнит-тесты.
А что можно тестировать в большом проекте без моков и фреймов? И вообще какой смысл тестировать то что вероятнее всего и не поламается.
Ну тоесть тестировать сеттер и геттер смысла нет, а вот парсер запроса или менеджер памяти есть.

Тесты не ловят ошибки, они только дают какуюто вероятность того что вы не создали новых. Ошибки же ловятся дебагерами и логгерами на стадии нагрузочного и системного тестирования. Особые гурманы еще делают фаззи-тесты.
А то, что архитектура позволяет без них :) Например, событийно-ориентированная архитектура моков не требует для тестов, только лишь того, что одно событие с одними параметрами вызвало другое с другими. Упрощая, что событие «мышка кликнула в таких-то координатах» вызвало событие «записать файл с таким-то содержимым по такому-то пути».

Вы, похоже, судите о по ситуации покрытия существующего кода тестами. В случае реального TDD тесты практически моментально «ловят» ошибки несоответствия функциональности кода грамотным формальным требованиям ТЗ (требования типа «программа должна считать сумму целых чисел неограниченной величины» пускай и формальные, но не грамотные на данном уровне развития науки и техники). Ловят их просто по определению TDD (если в самих тестах нет ошибки) — сначала формализуем требования к юниту, потом убеждаемся, что отсутствие кода тест не проходит, потом что только написанный код этим требованиям удовлетворяет, а значит ошибок в нем нет. Есть исключительные случаи, когда такая методика не работает (например, если результаты, хотя бы частные случаи, неизвестны), но в целом TDD гарантирует отсутствие ошибок, если сформулировано что это означает в терминах ВТ.
Вообще не очень понимаю смысла тестов. Если тест предназначен для того, чтобы отлавливать ошибки, которые программист предусмотрел, то зачем это нужно? Обычно все же код работает верно на тех данных, для которых он писался, а сыпется там, где что-то не предусмотрели. Если код сыпется на данных, под которые он делался без редких каких либо странных юз-кейсов, то это печально, тут уже мало что поможет. А как написать тест, который будет отлавливать ошибки на непредусмотренных кейсах, если кейс непредусмотрен? :)
Надеюсь, выразился понятно
Тест пишется потому, что код может начать сыпаться по непонятным причинам. Где-то обновили версию низкоуровневой библиотеки, где-то сделали рефакторинг и т.д. Тест проверяет уже известный набор входных данных и говорит — всё ок, с этой библиотекой\рефакторингом всё работает так же хорошо. Кроме того, написать тест очень полезно при нахождении какого-то бага: нашел баг — воспроизвел — написал тест — тест падает — пофиксил баг — тест проходит — комитнул код и тест — всё, больше этот баг не вылезет, а если и вылезет, то будет тут же замечен тестом.
в первуюочередь «простые тесты» отлавливают траблы неявно родившиеся во время добавления фич и т.д.
Тест фиксирует поведение кода. Когда будет «непредусмотренная» ошибка, то тест поможет не сломать в ходе её исправления то, что уже работало. Вернее сразу заметить поломку, а не после сдачи заказчику. Тест служит документацией того, что предусмотрели, а что нет. По сути тесты — это требования к функциональности выраженные в формальном форме. А тестирование — формальная проверка соответствия кода требованиям.
Давно занимает вопрос — как юнит-тестирование соотносится с программированием по контракту? Почему-то кажется, что то, что вы сейчас описали, можно отнести и туда, и туда. Помогите выбрать с чего начать причесывание большого не совсем красивого проекта.
Юнит-тестирование можно считать одним из методов проверки выполнения модулем контракта, выполнения им постусловий и инвариантов, а также соблюдения предусловий тех модулей, которые он вызывает как клиент. Но нужно понимать, что юнит-тесты не гарантируют соблюдение контракта в любой ситуации, а лишь показывают, что в некоторых он выполняется. С другой стороны, область применения юнит-тестов шире — они проверяют не только формальное соблюдение контракта методом, но и алгоритм его работы.

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

Если все не такк плохо, то начинать надо, пожалуй, с раздергивания приложения на более-менее изолированые части — современные IDE позволяют это делать относительно безопасно без покрытия тестами, — а затем покрывать эти изолированные части, хотя бы перед внесением в них изменений. В общем без соответствующего теста функциональность продукта меняться не должна, включая исправление ошибок. Иначе очень легко попасть в ситуацию, когда новой или измененная функциональность внедреятся быстрее, чем старая покрывается тестами. Тесты должны опережать изменения функциональности. Чтобы работа по покрытию тестами (юнит) не казалась неподъемной, нужно четко себя ограничивать что нужно тестировать, а что нет. Например, нет нужды тестировать как СУБД выполняет SQL запросы, достаточно проверить, что запрос соответствует ожидаемому и/или подсунуть ожидаемые методом данные.
Именно лень толкает программистов писать код, который будет потом генерировать рутинный код за них :)
Метапрограммирование придумали лентяи. И еще не родились такие лентяи, которые смогут построить автоматический конвертер галлюцинаций заказчика в жесткую формальную систему.
Самое главное, чтобы потом было не лень переписывать вашу груду тестов, когда поменяются условия и придется вносить измнения дважды: в сам код, да еще тесты править.
Получается, самые главные лентяи — авторы современных продвинутых IDE.
Не указаны главные, имхо, свойства TDD в контексте лени:
— Слабосвязанная и сильносвязная архитектура — лень иниициировать кучу объектов, параметров, стабов и моков, а потому число внешних зависимостей уменьшается, а внутренних увеличивается.
— Соблюдение DRY — лень писать повторяющиеся тесты для повторяющейся функциональности, проще один раз её выделить и один раз протестировать
— Соблюдение KISS и YAGNI — лень придумывать что-то «на всякий случай», ведь это ещё и тест придется придумывать и писать на этот случай
— Декомпозиция задач (а-ля «unix-way») — лень приступать к большой и сложной задаче, не получая немедленный результат. А вот если её формально разбить на подзадачи, то мелкие подзадачи делать уже не так лень, ведь есть немедленный и, что немаловажно, формально верифицируемый результат.
Для меня лень была и остается основным определителем соотношения трудозатрат к конечной цели. Это чувство никогда не подводило — если лень, значит есть необходимость пересматривать пути решения, возвращаясь к задаче, возможно изменились условия или появились лучшие варианты….

Если смотреть на методологию как на один способов снизить трудозатраты, то обнаружим различия в этом соотношении отдельного программиста и группы, непременно нужно искать баланс, а это не учитывается даже в современных методологиях и должно ложится на плечи ПМ, а ему всегда лень ))).
Ну, иногда формальный вывод алгоритма (или, в частном случае, цикла) из предусловия и инварианта — самый ленивый способ. При некотором опыте он позволяет вообще не думать как раз в сложных случаях и писать код, в принципе не требующий никаких тестов.
Можно пример? Можно на Эйфеле :)
Так лень же, да и на эйфеле я не пишу :) Но классический пример — двоичный поиск — отлично разобран у Бентли в его «Жемчужинах» (глава «как писать правильные программы»). Ну и вся книжка Гриса («Наука программирования») об этом. Это только сначала кажется, что оно трудно и непривычно, а потом начинает получаться и потихоньку становится тривиальным навыком.
Кстати, в лесу на пеньке нет интернета, начальства, соседей и других отвлекающих факторов.
Так что код, написанный там, вполне может быть лучше
Sign up to leave a comment.