Comments 225
Мне приходилось слышать банальное «Лень»
банальный пример: изменилась форма 2НДФЛ разработчики прислали новую конфигурацию с ее реализацией, как написать такой тес чтобы он определил что какое то из полей заполняется не верно, при том что алгоритм изменился?
UFO just landed and posted this here
«поэтому ушел» — правильно сделали. Если организация не поддерживает пути разработки, кроме «быстро и за копейки» — нахрена там работать?
причем тут вообще заказчики? все эти штуки — паттерны вместе с юнит-тестами, разработчики придумали для себя, чтобы быстрее работалось. заказчикам про них вовсе знать не обязательно
А мне казалось, что основное их назначение упрощение и ускорение не собственно разработки, а дальнейшей поддержки и развития.
одно другому не мешает
Мешает.
Куча действий требуют затрат СЕЙЧАС, а пользу приносят ПОТОМ.
Это инвестиции, и компании, которые не готовы инвестировать в свои процессы, всегда находятся в хаотично-болотистом состоянии.
Заметьте, что если в компании принято говорить «сейчас у нас нет не это времени», то время так никогда и не появляется.
Куча действий требуют затрат СЕЙЧАС, а пользу приносят ПОТОМ.
Это инвестиции, и компании, которые не готовы инвестировать в свои процессы, всегда находятся в хаотично-болотистом состоянии.
Заметьте, что если в компании принято говорить «сейчас у нас нет не это времени», то время так никогда и не появляется.
«Сейчас у нас нет на это времени» === («У нас нет компетентных людей способных справиться с поставленной задачей» | «Мы не в состоянии заинтересовать компетентных сотрудников заняться этой задачей»)
UFO just landed and posted this here
А Лень как известно — двигатель прогресса!!! )))
1. Если код проходит юнит тесты, это абсолютно не значит, что он правильный. Юниты никогда не покрывают всех возможных состояний и вариантов исполнения кода. Более того, тесты, проверяющие «граничные» и «критические» значения остаются в большинстве случаев «за кадром». Поэтому сложные и редкие ошибки не выявляются юнитами.
2. Любые утверждения, что юниты экономят время, безосновательны, потому как юнит тесты не исключают дебага. А если сделан пошаговый дебаг, большого смысла в юнит тестах я не вижу. Все говорят об эффективности и правильности юнитов, но никто до сих пор наглядно так и не доказал это.
3. Заранее просчитать результат выполнения сложной функции не представляется возможным, не выполнив ее перед этим. Только пошаговый контроль в дебаге сможет выявить, что вычисленный результат верен. Если юнит не сработал, так или иначе придется дебагить.
4. Юнит тесты сложны в поддержке. Код меняется по мере разработки — тонны юнитов летят в корзину. Если Вы делаете одно простое изменение в коде, Вам нужно перелопатить десяток тестов.
Безусловная полезность юнитов и TDD является в том, что самого начала заставляет быдлокодеров заботиться о компонентной модели кода и декомпозиции функционала.
1. Если код проходит юнит тесты, это абсолютно не значит, что он правильный. Юниты никогда не покрывают всех возможных состояний и вариантов исполнения кода. Более того, тесты, проверяющие «граничные» и «критические» значения остаются в большинстве случаев «за кадром». Поэтому сложные и редкие ошибки не выявляются юнитами.
2. Любые утверждения, что юниты экономят время, безосновательны, потому как юнит тесты не исключают дебага. А если сделан пошаговый дебаг, большого смысла в юнит тестах я не вижу. Все говорят об эффективности и правильности юнитов, но никто до сих пор наглядно так и не доказал это.
3. Заранее просчитать результат выполнения сложной функции не представляется возможным, не выполнив ее перед этим. Только пошаговый контроль в дебаге сможет выявить, что вычисленный результат верен. Если юнит не сработал, так или иначе придется дебагить.
4. Юнит тесты сложны в поддержке. Код меняется по мере разработки — тонны юнитов летят в корзину. Если Вы делаете одно простое изменение в коде, Вам нужно перелопатить десяток тестов.
Безусловная полезность юнитов и TDD является в том, что самого начала заставляет быдлокодеров заботиться о компонентной модели кода и декомпозиции функционала.
Тесты — суть выражение функциональных спецификаций. Если в тесте не отражены (явно или же очевидным образом) какие-то условия, считайте что на них нет постановки. В самом деле, постановка может быть не полной. Но в этом виновата лишь ваша лень — при чем тут TDD? :) Кстати, тут могут быть полезны вот такие феньки, которые выводят лейаут теста на «человеческом» языке — обратно, в виде спецификации.
Если вы делаете одно простое изменение в коде, и вам нужно перелопатить десяток тестов, это означает что вы поменяли шаги местами. сначала меняется один тест — затем вносится изменение в код.
В процессе выполнения теста создаются идеальные условия для дебага.
Если одно и то же изменение в коде вызывает каскадное изменение других тестов, это является сигналом о сильной связанности — т.е. ваша система тестов попросту плохо спроектирована. (сюрприз, чтобы тесты было легко поддерживать, их тоже необходимо вдумчиво проектировать — так же тщательно как и любой другой код. поэтому как раз-таки для «быдлокодеров» от ни нет ни какой пользы).
Если вы делаете одно простое изменение в коде, и вам нужно перелопатить десяток тестов, это означает что вы поменяли шаги местами. сначала меняется один тест — затем вносится изменение в код.
В процессе выполнения теста создаются идеальные условия для дебага.
Если одно и то же изменение в коде вызывает каскадное изменение других тестов, это является сигналом о сильной связанности — т.е. ваша система тестов попросту плохо спроектирована. (сюрприз, чтобы тесты было легко поддерживать, их тоже необходимо вдумчиво проектировать — так же тщательно как и любой другой код. поэтому как раз-таки для «быдлокодеров» от ни нет ни какой пользы).
Я не пишу юнит тесты — потому что практически не возможно автоматически пользоваться ими в гетерогенной с точки зрения устройств среде. я могу запустить проверку как правило только на эмуляторе, я не имею возможно автоматически продеплоймить и запустить на всех целевых устройствах.
Я не пишу юнит тесты — потому что они не работают (без собственно реализации) в гетерогенной с точки зрения языков программирования среде. Типичный пример — код с тесным взаимодействием классов и функция на языках c/c++, java и javascript, причем javascritp во внутреннем браузере который так же на писан на c/++ и java.
Я не пишу юнит тесты — потому что более существенная часть времени тратиться на баги которые не могут быть (или это крайне сложно) обнаружены в автоматическом режим. Типичные примеры — после изменения скорости движения у главного героя проскальзывают ноги, плашка под полем ввода сдвинута на 2 пикселя влево по сравнению с redlines, opengl эффект затухания меню стал выглядеть некрасиво, после перевода картинка в 8 бит градиенты стали выглядеть хуже.
Я не пишу юнит тесты потому что — очень часто не возможно формализовать требования к коду пока он не написан. Типичный пример, когда задача ставится следующим образом — написать физику управления машинкой чтобы перло.
Я не пишу юнит тесты — потому что они не работают (без собственно реализации) в гетерогенной с точки зрения языков программирования среде. Типичный пример — код с тесным взаимодействием классов и функция на языках c/c++, java и javascript, причем javascritp во внутреннем браузере который так же на писан на c/++ и java.
Я не пишу юнит тесты — потому что более существенная часть времени тратиться на баги которые не могут быть (или это крайне сложно) обнаружены в автоматическом режим. Типичные примеры — после изменения скорости движения у главного героя проскальзывают ноги, плашка под полем ввода сдвинута на 2 пикселя влево по сравнению с redlines, opengl эффект затухания меню стал выглядеть некрасиво, после перевода картинка в 8 бит градиенты стали выглядеть хуже.
Я не пишу юнит тесты потому что — очень часто не возможно формализовать требования к коду пока он не написан. Типичный пример, когда задача ставится следующим образом — написать физику управления машинкой чтобы перло.
вы не пишете юнит-тесты, потому что не понимаете, что это такое и для чего оно нужно, а вовсе не потому, что вы крутой разработчик игр
1) юнит тесты могут работать в гетерогенной среде, я вас в этом уверяю, т.к. я это делал
2) юнит тесты могут работать даже в смеси javascript/java/c++, просто надо правильно дизайнить систему, разделять эти уровни, делать их тестируемыми по отдельности, делать дизайн всей системы mockable, писать эти самые моки и тестировать end-to-end
3) именно поэтому надо писать юнит тесты — чтобы не тратить время на баги, которые юнит тестами *могут* быть обнаружены.
4) а вот это уже просто потому, что вы, вероятно, не очень опытный программист — это приходит со временем, со сданными (и заваленными!) проектами, с опытом.
Overall, мне кажется, что в вас играет некий юношеский максимализм — «Юнит-тесты не нужны, потому что мои задачи для них слишком сложны». Это полностью повторяет первую отговорку, приведенную топиккастером.
Q.E.D.
2) юнит тесты могут работать даже в смеси javascript/java/c++, просто надо правильно дизайнить систему, разделять эти уровни, делать их тестируемыми по отдельности, делать дизайн всей системы mockable, писать эти самые моки и тестировать end-to-end
3) именно поэтому надо писать юнит тесты — чтобы не тратить время на баги, которые юнит тестами *могут* быть обнаружены.
4) а вот это уже просто потому, что вы, вероятно, не очень опытный программист — это приходит со временем, со сданными (и заваленными!) проектами, с опытом.
Overall, мне кажется, что в вас играет некий юношеский максимализм — «Юнит-тесты не нужны, потому что мои задачи для них слишком сложны». Это полностью повторяет первую отговорку, приведенную топиккастером.
Q.E.D.
Было бы круто пару примеров привести.
примеров чего?
1) юнит тесты могут работать в гетерогенной среде, я вас в этом уверяю, т.к. я это делал
Вообще это сильно зависит от характера приложений. Но общий принцип один и тот же – делать mockable как можно больше частей кода, чтобы можно было отдельно протистировать взаимодействие как таковое (это сложно, но не очень сложно), и отдельно — функциональность компонент.
Ну и разумеется, если архитектура позволяет (читай: дизайнилось изначально с расчетом на автоматизированное тестирование), часто можно в режиме big test поднять небольшой тестовый environment даже на нескольких машинах, сервер, клиент, прогнать batch-request, посмотреть, что получится и сравнить с тем, что должно.
Вот с чем я совершенно не сталкивался — это тестирование браузерных приложений. Знаю, что это как-то делается, но поскольку UI я не занимаюсь ни в каком виде, с этим я не работал.
Ну и разумеется, если архитектура позволяет (читай: дизайнилось изначально с расчетом на автоматизированное тестирование), часто можно в режиме big test поднять небольшой тестовый environment даже на нескольких машинах, сервер, клиент, прогнать batch-request, посмотреть, что получится и сравнить с тем, что должно.
Вот с чем я совершенно не сталкивался — это тестирование браузерных приложений. Знаю, что это как-то делается, но поскольку UI я не занимаюсь ни в каком виде, с этим я не работал.
Я чаще всего слышу отговорку такого свойства: «Если я напишу тесты, кто напишет тесты на тесты». То есть, есть ли гарантия, что тесты работают правильно? Я сам много раз сталкивался с ситуациями, когда сами тесты содержали ошибки; иногда крошечная механическая опечатка в тесте из серии
for(i=0; i<42; ++i); {units[i]->test_yourself()}
может создать ложную уверенность, что всё работает отлично.
for(i=0; i<42; ++i); {units[i]->test_yourself()}
может создать ложную уверенность, что всё работает отлично.
Двойная проверка. Вероятность того, что одна и та же ошибка будет допущена в 2-х местах (и в тестах и в коде) — во много крат ниже.
Во-первых, а почему вы решили остановиться на двойной? Может лучше тройная? Согласитесь, что даже десятирная — не панацея. Причём, — внимание, — все следующие проверки не проверяют код! Они проверяют проверяльщиков. Если единственная первая проверка что-то упустила, все остальные не помогут.
Во-вторых, почему вы говорите про одну и ту же ошибку? Ошибки могут быть разные. Достаточно того, чтобы одна ошибка скрывала другую. Самый простой вариант, тест просто не тестирует некий аспект, скажем, не пробует везде подставить нули и не детектирует деление на ноль, которое выползает в реальной жизни.
Во-вторых, почему вы говорите про одну и ту же ошибку? Ошибки могут быть разные. Достаточно того, чтобы одна ошибка скрывала другую. Самый простой вариант, тест просто не тестирует некий аспект, скажем, не пробует везде подставить нули и не детектирует деление на ноль, которое выползает в реальной жизни.
>>Во-первых, а почему вы решили остановиться на двойной?
Потому что есть 2 стороны: наша система (программа) и внешний мир (пользователь или другая система). С помощью тестов мы эмулируем внешний мир. Третьей стороны нет. Была бы третья — можно было бы делать тройную проверку.
Алгоритм «внешнего мира» не является полным аналогом алгоритма системы.
>>Достаточно того, чтобы одна ошибка скрывала другую.
Бывает и такое. Здесь важна вероятность. По вероятности отпишу ниже.
Лично мне тесты помогают при рефакторинге. Хочу улучшить код (привести к ) — а он потом перестает работать правильно (какая-нибудь мелочь). Вот такие мелочи тесты находят быстро.
Потому что есть 2 стороны: наша система (программа) и внешний мир (пользователь или другая система). С помощью тестов мы эмулируем внешний мир. Третьей стороны нет. Была бы третья — можно было бы делать тройную проверку.
Алгоритм «внешнего мира» не является полным аналогом алгоритма системы.
>>Достаточно того, чтобы одна ошибка скрывала другую.
Бывает и такое. Здесь важна вероятность. По вероятности отпишу ниже.
Лично мне тесты помогают при рефакторинге. Хочу улучшить код (привести к ) — а он потом перестает работать правильно (какая-нибудь мелочь). Вот такие мелочи тесты находят быстро.
Принцип Паретто: 20% затрат, 80% результата.
И следующая за этим принципом приоритезация задач.
Когда (и ЕСЛИ) у Вас будет абсолютно свободное время, Вы можете сделать тройную, четверную, и какую угодно проверку. Но начинать всё равно надо с наиболее приоритетного уровня проверки, то есть первого.
И следующая за этим принципом приоритезация задач.
Когда (и ЕСЛИ) у Вас будет абсолютно свободное время, Вы можете сделать тройную, четверную, и какую угодно проверку. Но начинать всё равно надо с наиболее приоритетного уровня проверки, то есть первого.
Комментарии из серии «книгу не читал, но хочу заметить». CRAP наше все.
во-скока — во-скока?!
Во много крат. Не в 2 раза, а именно во много крат. Парадокс, да?
Допустим, в моей системе 100 логических действий, каждое из которых может быть написано с ошибкой (человеческий фактор). В среднем я делаю 1 ошибку на 100 действий. Т.е. в этих 100 действиях есть 1 ошибка (возьмем средний случай).
Если добавить тесты, то в них так-же будет 1 ошибка (так-же 100 действий).
Какова вероятность того, что ошибка будет в одном и том-же месте и в тестах и в программе (речь о машинальной ошибке)? Получается 1 из 10000, верно? Другими словами качество кода повысится в 100 раз, хотя времени затратим только в 2 раза больше.
Поправьте меня, если я ошибся.
Допустим, в моей системе 100 логических действий, каждое из которых может быть написано с ошибкой (человеческий фактор). В среднем я делаю 1 ошибку на 100 действий. Т.е. в этих 100 действиях есть 1 ошибка (возьмем средний случай).
Если добавить тесты, то в них так-же будет 1 ошибка (так-же 100 действий).
Какова вероятность того, что ошибка будет в одном и том-же месте и в тестах и в программе (речь о машинальной ошибке)? Получается 1 из 10000, верно? Другими словами качество кода повысится в 100 раз, хотя времени затратим только в 2 раза больше.
Поправьте меня, если я ошибся.
Парадокса нет. Всё зависит от того, как вы меряете качество кода.
Без тестов ваш код был на 99% правильным, с тестами — на 99.99%. Качество выросло всего на 1 процент, а времени потрачено в 2 раза больше.
Без тестов ваш код был на 99% правильным, с тестами — на 99.99%. Качество выросло всего на 1 процент, а времени потрачено в 2 раза больше.
— Какова вероятность, что выйдя на улицу вы встретите динозавра?
Вы рассуждаете как некоторые женщины:
— Нуу, 50/50 — либо встречу, либо не встречу!
Вы рассуждаете как некоторые женщины:
— Нуу, 50/50 — либо встречу, либо не встречу!
Всё зависит от точки зрения, вас интересует вероятность наличия ошибки, собеседника — вероятность её отсутствия :) Увеличивая одну в два раза, мы не уменьшаем другую в два раза. Анекдот про 50/50 не уместен тут.
Чего? Серьёзно?! Ё-моё, вся теория вероятности коту под хвост.
Если вероятность события A = 0.7, то вероятность того, что событие A не случится = 1 — 0.7 = 0.3
Вы сами внимательно прочитали комментарий, который вы поддерживаете?
Код на «99% правильный», стал на «99.99%» правильным. Это вообще чего такое?
Эти цифры взяты из каких натуральных соображений?
Код может быть правильным и на 100%, но при его изменении существует достаточно большая вероятность неверного конечного результата. Тесты же позволяют компенсировать эту вероятность, отчего общая вероятность ошибки становится (по доказанной теории вероятности) A*B (вероятность возникновения 2х параллельных событий), а не A+0.99% (вероятность ошибки в коде + какая-то-с-потолка-взятая-процентность).
Если вероятность события A = 0.7, то вероятность того, что событие A не случится = 1 — 0.7 = 0.3
Вы сами внимательно прочитали комментарий, который вы поддерживаете?
Код на «99% правильный», стал на «99.99%» правильным. Это вообще чего такое?
Эти цифры взяты из каких натуральных соображений?
Код может быть правильным и на 100%, но при его изменении существует достаточно большая вероятность неверного конечного результата. Тесты же позволяют компенсировать эту вероятность, отчего общая вероятность ошибки становится (по доказанной теории вероятности) A*B (вероятность возникновения 2х параллельных событий), а не A+0.99% (вероятность ошибки в коде + какая-то-с-потолка-взятая-процентность).
Всё согласно теорверу:
Если вероятность отсутствия ошибки 0.99, то вероятность её наличия 0.01. Уменьшаем вероятность наличия ошибки в 100 раз — получаем 0.0001, не будете же вы утверждать, что вероятность отсутствия ошибки стала 99 в таком случае (0.99*100)? Правильно, вероятность отсутствия ошибки увеличилась всего в (1-0.0001)/0.99=1,01 раза или на 1%.
Каким-то образом оценили вероятное количество ошибок в коде (есть методики, например намеренное введение ошибок в код, с последующим его анализом другими программистами/тестировщиками, соотношение заранее известных и неизвестных ошибок среди найденных ими позволяет делать выводы о вероятном оставшемся количестве неизвестных) в 1% от всего кода. Покрыли код тестами на 100%, оценили также количество ошибок в 1% от количества кода теста. Есть, имхо, основания утверждать, что вероятное количество ошибок в коде уменьшилось в 100 раз с 1% до 0.01%. Возможно не точно, теорию ошибок и т. п. не изучал, но натуральные соображения такие.
А в том, что основное назначение тестов не повышение вероятности отсутствия необнаруженных ошибок в существующем коде, а уменьшение вероятности образования новых необнаруженных ошибок при изменении кода, я полностью с вами согласен. TDD же, как я её понял, базируется на том, что код надо покрывать тестами ещё до его написания и, соответственно, уменьшать вероятность появления необнаруженных ошибок с самого начала, условно считая, что написание кода является изменением пустого кода :)
Если вероятность отсутствия ошибки 0.99, то вероятность её наличия 0.01. Уменьшаем вероятность наличия ошибки в 100 раз — получаем 0.0001, не будете же вы утверждать, что вероятность отсутствия ошибки стала 99 в таком случае (0.99*100)? Правильно, вероятность отсутствия ошибки увеличилась всего в (1-0.0001)/0.99=1,01 раза или на 1%.
Каким-то образом оценили вероятное количество ошибок в коде (есть методики, например намеренное введение ошибок в код, с последующим его анализом другими программистами/тестировщиками, соотношение заранее известных и неизвестных ошибок среди найденных ими позволяет делать выводы о вероятном оставшемся количестве неизвестных) в 1% от всего кода. Покрыли код тестами на 100%, оценили также количество ошибок в 1% от количества кода теста. Есть, имхо, основания утверждать, что вероятное количество ошибок в коде уменьшилось в 100 раз с 1% до 0.01%. Возможно не точно, теорию ошибок и т. п. не изучал, но натуральные соображения такие.
А в том, что основное назначение тестов не повышение вероятности отсутствия необнаруженных ошибок в существующем коде, а уменьшение вероятности образования новых необнаруженных ошибок при изменении кода, я полностью с вами согласен. TDD же, как я её понял, базируется на том, что код надо покрывать тестами ещё до его написания и, соответственно, уменьшать вероятность появления необнаруженных ошибок с самого начала, условно считая, что написание кода является изменением пустого кода :)
Коль, перечитай тервер.
Я его помню. И есть конкретное правило: если в момент времени вероятность возникновения события A равна p1 и события B равна p2, и события равновероятны, то общая вероятность возникновения этих событий равна p1*p2 (например, подбрасывание 2х монет и выпадение на обоих решки, 0.5*0.5 = 0.25). Это же не высшая математика, всё просто. Поэтому, если при изменении кода вероятность возникновения ошибки равна p1, в то же время вероятность ошибки в тесте на этот код равна p2, всё абсолютно аналогично. Вопрос ведь не в том «какова вероятность наличия ошибки в коде при наличии теста», а «какова вероятность ошибки и к воде и в тесте на этот код».
Попробуй посчитать вероятность возникновения ошибки по другому: у тебя в программном модуле есть N методом и в каждом из них ты можешь сделать ошибку с вероятностью, пропорциональной количеству строчек кода, реализующих бизнес-логику. Вероятность допустить ошибку является композицией от вероятностей сделать ошибку в этих N методах. В случае TDD ты пишешь N тестов, чтобы покрыть функциональность методов (при правильном применении ТДД количество тестов будет соответствовать количеству методов, при неправильном — количество тестов будет больше, чем количество методов). Соответственно, вероятность возникновения ошибок в тестах считается так же. Так-как функциональность методов пишется после создания тестов, то вероятность возникновения первичной ошибки в тестах считается первичной, то при возникновении ошибки в тесте реализация уже будет неправильной. По этому вероятность ошибки реализации программы в целом будет считаться как сумма вероятностей от ошибки в тесте и одновременной ошибки и в тесте и в реализации.
Соответственно, при увеличении строк кода в тесте и увеличении N вероятность ошибки увеличивается. Откуда были получены эти «в разы» мне, лично, непонятно.
Соответственно, при увеличении строк кода в тесте и увеличении N вероятность ошибки увеличивается. Откуда были получены эти «в разы» мне, лично, непонятно.
>>Без тестов ваш код был на 99% правильным, с тестами — на 99.99%. Качество выросло всего на 1 процент, а времени потрачено в 2 раза больше.
Совершенно верно. Нужно только решить насколько критичен этот 1% для конкретного проекта. Если 1% ошибок выльется в процент потерянных денег — то весьма критично.
Совершенно верно. Нужно только решить насколько критичен этот 1% для конкретного проекта. Если 1% ошибок выльется в процент потерянных денег — то весьма критично.
Прочитайте пожалуйста: habrahabr.ru/blogs/tdd/112685/#comment_3611572
По TDD — сначала тест не должен пройти — он должен быть красным. После того, как вы добавите код, запустите еще раз тест — он должен быть успешен. Таким образом если у вас он до написания кода зеленый, или после написания кода красный — значит тест не правильный (при условии что код верен).
Вот ответ на ваш вопрос: habrahabr.ru/blogs/tdd/112851/
Насколько я помню, одно из первых правил написания тестов — «make it fail».
Т.е. при написании теста нам нужно добиться обоих случаев — и чтобы он выполнялся, и чтобы он валился.
Но ошибки в тестах, тем не менее никто не отменял. И частичным тестированием юнит-тестов будут заниматься интеграционные (к которым так же применяем правило «make it fail»).
Т.е. при написании теста нам нужно добиться обоих случаев — и чтобы он выполнялся, и чтобы он валился.
Но ошибки в тестах, тем не менее никто не отменял. И частичным тестированием юнит-тестов будут заниматься интеграционные (к которым так же применяем правило «make it fail»).
Давно уже перешёл на TDD, и очень доволен. Но, к сожалению, не всех могу убедить использовать юнит тестирование. Чаще всего отговорки: нет у меня времени писать тесты, да и не помогут они.
TDD абсолютно не панацея. нужно найти баланс между следованию ТДД во всем и построением архитектуры приложения изначально.
Вообще TDD это действительно не панацея, а часть методологии XP — но там upfront построение архитектуры это просто выброшенное в унитаз время, чем и является.
Я прошел горький опыт от проектирования систем upfront, когда пол года постоянной работы трех архитекторов только над проектированием вылился в бешенное разочарования из за изменений требований на середине двух годичной разработки.
Проект пришлось переписывать заново, в этот раз на проектирование upfront потратили две недели ровно — даль итеративный процесс разработки архитектуры. Проект получилися очень простым, очень maintainable и все, включая архитекторов, просто в восторге от такого подхода.
Но, без TDD, без итераций, без быстрого feedback'а и понимания, что надо приветствовать изменения, а не отвергать — без всего этого не возможен был бы успех.
Я прошел горький опыт от проектирования систем upfront, когда пол года постоянной работы трех архитекторов только над проектированием вылился в бешенное разочарования из за изменений требований на середине двух годичной разработки.
Проект пришлось переписывать заново, в этот раз на проектирование upfront потратили две недели ровно — даль итеративный процесс разработки архитектуры. Проект получилися очень простым, очень maintainable и все, включая архитекторов, просто в восторге от такого подхода.
Но, без TDD, без итераций, без быстрого feedback'а и понимания, что надо приветствовать изменения, а не отвергать — без всего этого не возможен был бы успех.
заметьте, я не утверждал, что ТДД зло и не используйте его. Я лишь говорил, что нужен баланс. Вы в своем случае его нашли и получился отличный проект. Если следовать чисто ТДД методикам, то получится тоже великое разочарование по той причине, что у вас порсто не будет архитектуры вменяемой
«Если следовать чисто ТДД методикам, то получится тоже великое разочарование по той причине, что у вас порсто не будет архитектуры вменяемой»
Непонимаю, откуда вы берете это утверждение? Почему вы считаете, что если следовать TDD у вас не будет архитектуры? Просто в голове не укладывается, чем написания тестов перед реальным кодом может помешать нормальной архитектуре?
Наоборот, если идти TDD way эволюционировать архитектуру намного проще, чем без TDD, ибо TDD требует движение маленькик шагами, когда после вашего каждого изменения вы уверены, что проект будет работать. При стандартном изменении архитектуры все меняется большим скопом, потом долго и упорно тестируется и правяться (или не правяться) тесты.
Непонимаю, откуда вы берете это утверждение? Почему вы считаете, что если следовать TDD у вас не будет архитектуры? Просто в голове не укладывается, чем написания тестов перед реальным кодом может помешать нормальной архитектуре?
Наоборот, если идти TDD way эволюционировать архитектуру намного проще, чем без TDD, ибо TDD требует движение маленькик шагами, когда после вашего каждого изменения вы уверены, что проект будет работать. При стандартном изменении архитектуры все меняется большим скопом, потом долго и упорно тестируется и правяться (или не правяться) тесты.
UFO just landed and posted this here
> «тупо Кодер»
Называйте вещи своими именами, чего уж там. Зачем эвфемизмы выдумывать?
Называйте вещи своими именами, чего уж там. Зачем эвфемизмы выдумывать?
Когда спецификация заморожена, то тесты могут покрывать спецификацию. А если функциональность постоянно меняется, но приходится менять логику и тесты. И в тестах появляются баги, так что хоть пиши тесты для тестов.
тест == спецификация. меняются требования, меняется тест, меняется логика — в такой последовательности.
Меняется функциональность -> меняется тест -> код переписывается чтобы этот тест «прошел».
Частенько, из тестов проще понять что должен делать код, чем из самого кода.
Равно как, и сначала переписать тесты под новую спецификацию, бывает проще чем сразу править код.
Частенько, из тестов проще понять что должен делать код, чем из самого кода.
Равно как, и сначала переписать тесты под новую спецификацию, бывает проще чем сразу править код.
Бывает так что тест сильно зависит от логики, поэтому я не могу написать тест, пока не определюсь с названиями методов, интерфейсов. А эти названия, структуры приходят во время написания логики.
в этом и специфика тдд. что когда вы пишите тест вы продумываете сразу названия методов которые он будет тестировать, интерфейсы и тп.
Я не знаю как в совсем тру TDD. Но если я начинаю писать какой то код, то последовательность такая:
1. Костяк системы — внешнее API, классы модели, классы представления, интерфейсы сервисов. Все это почти без внутренностей.
2. Тесты фиксирующие будущее поведение системы.
3. Наполняю вещи из пункта 1 кодом, пишу внутренние служебные классики и тесты для них.
Подход итеративный. Т.е. когда мы спускаемся вниз к внутренним классам, то для них эти 1 шага повторяются.
1. Костяк системы — внешнее API, классы модели, классы представления, интерфейсы сервисов. Все это почти без внутренностей.
2. Тесты фиксирующие будущее поведение системы.
3. Наполняю вещи из пункта 1 кодом, пишу внутренние служебные классики и тесты для них.
Подход итеративный. Т.е. когда мы спускаемся вниз к внутренним классам, то для них эти 1 шага повторяются.
я не пишу юнит тесты, потому что в 1с это нереально
Уважаемый посетитель!
Мы благодарны за интерес к сайту pcmag.ru, однако, как показывает статистика, 71% посетителей, приходящих по ссылке с сайта habrahabr.ru, покидает нас спустя 5-10 с.
Заботясь о вашем комфорте и удобстве серфинга, мы хотели бы предложить ряд ссылок, которые, согласно статистическим данным, могли бы быть более интересны:
«Жучарко с сяжками и скаковыми ногами»
«Дом-2 — официальный сайт реалити-шоу на канале ТНТ»
«Мой мир@Mail.Ru»
«РАНЕТКИ — официальный сайт»/
«Comedy Club Официальный сайт»
Для продолжения сеанса работы и перехода на сайт, вам достаточно повторно кликнуть по кнопке Go (или «Переход» для русских версий популярных браузеров) или перейти в строку адреса и нажать Enter.
Если у вас имеются вопросы и пожелания, вы можете связаться с администрацией сайта по адресу newsdesk@pcmag.ru.
Решение есть на сайте ранеток.
Ну, я думаю, не только программисты знают, как зайти на сайт, не оставив referer? А?
Интересно, кстати, чеи это вызвано? :)
Из этого я понял только то, что PCMAG делают идиоты.
Проблема в том, что число вариантов слишком велико, взять тот же расчет себестоимости/партионный учет/РАУЗ, которые настраиваются десятками параметров. чтобы подготовить тестовые данные, охватывающие все случаи, уйдет не один месяц.
(а потом еще нужно будет написать тесты для проверки правильности работы тестов и т.д.)
(а потом еще нужно будет написать тесты для проверки правильности работы тестов и т.д.)
Незнаю по поводу 1С, но порадовала отмазка про тестовые данные.
Вы именно сначало должны их подготовить, а уж потом писать расчеты. При чем необязательно сразу подготавливать огромный набор, в TDD процесс итеративный.
Вы именно сначало должны их подготовить, а уж потом писать расчеты. При чем необязательно сразу подготавливать огромный набор, в TDD процесс итеративный.
вот есть типовая конфигурация. (более 40 мб кода в ТХТ). нам нужно сделать определенную доработку, причем таким образом, чтобы обновление этой типовой не сильно затруднялось. + прикрутить каким-то образом туда еще программную часть для юнит-тестирования. + вбить данные, которых для моделирования реальных ситуаций учета нужно реально много (десятки и сотни документов/элементов справочников для проверки чего-то одного. + эталонные результаты выполнения, которые вообще непонятно, как хранить)
Ну в данном случае это класический пример legacy кода — где либо забиваем на все, либо на доработки пишем unit test'ы, но только там, где это принесет бенефиты.
Тут опять таки нужен баланс — есть так называемая теория technology debt, то есть когда изменения вносятся adhoc и забивают на всякие тесты — недоработки в тестировании или shortcut'ы всякие увеличивают technology debt, который придется когда нибудь платить. Если technology debt изменения конфигурации 1С совершенно несравним с деланием всего правильно и в дальнейшем исправления все этого technology debt все равно в разы меньше effort'ов на создания тестов — тогда да, в топку TDD в данном случае.
Тут опять таки нужен баланс — есть так называемая теория technology debt, то есть когда изменения вносятся adhoc и забивают на всякие тесты — недоработки в тестировании или shortcut'ы всякие увеличивают technology debt, который придется когда нибудь платить. Если technology debt изменения конфигурации 1С совершенно несравним с деланием всего правильно и в дальнейшем исправления все этого technology debt все равно в разы меньше effort'ов на создания тестов — тогда да, в топку TDD в данном случае.
а как правильность расчетов проверяется сейчас?
юнит-тестирование помогает создать хороший дизайн.
Cтажёр и младший программист с радостью начинают писать юнит тесты, чтобы понять, что такое хороший дизайн (сразу заметен «рост»).
Бывалый программист говорит, что и так всё знает («лень») и спихивает всё на тестеров (сразу заметно, кто плодит больше багов и отлаживает по F5).
Eсли команда примет решение (писать тесты), отговорки должны быть неуместны имхо.
Очень часто, что из коммерческих соображений, в списке «время», «функциональность», «качество», дл компании качество на последнем месте. например важно выйти с новой верисей раньше конкурентов
«Это отговорка номер один.»
Отговорки — удел программистов, а решение сколько времени уделять на тестирование, сколько тестеров держать — удел начальства.
Фирма должна делать деньги. Хороший софт — не самоцель, а средство.
Я сам пишу как раз юнит тесты :)
Но время на написание юнит тестов и количество тестеров в команде — это в конце концов бизнес решения.
Они могут быть ошибочными (если из за плохого качества клиенты откажутся от софта), или правильные (если через месяц выпустили следующую версию с исправлениями, но конкурентов опередили), но это бизнес решения
Фирма должна делать деньги. Хороший софт — не самоцель, а средство.
Я сам пишу как раз юнит тесты :)
Но время на написание юнит тестов и количество тестеров в команде — это в конце концов бизнес решения.
Они могут быть ошибочными (если из за плохого качества клиенты откажутся от софта), или правильные (если через месяц выпустили следующую версию с исправлениями, но конкурентов опередили), но это бизнес решения
Я к тому, что время сэкономленное на тестах часто уходит на унылый дебуггинг в поисках ошибок.
Это про первую версию софта.
Потом проходит время. Софт обрастает функционалом. И в какой то момент нужно вносить изменения в вещи сделанные в первой версии (без тестов) И тут мы понимаем, что мы уже забыли что там и как работает, и внося изменения без тестов вынуждены проверять все вручную или ловить баги или писать тесты на существующий функционал.
Конечно, это не относится к случаю, когда за 2 выходных нужно изготовить прототип системы (впрочем базовые тесты и тут помогут).
Это про первую версию софта.
Потом проходит время. Софт обрастает функционалом. И в какой то момент нужно вносить изменения в вещи сделанные в первой версии (без тестов) И тут мы понимаем, что мы уже забыли что там и как работает, и внося изменения без тестов вынуждены проверять все вручную или ловить баги или писать тесты на существующий функционал.
Конечно, это не относится к случаю, когда за 2 выходных нужно изготовить прототип системы (впрочем базовые тесты и тут помогут).
Пусть начальство решает по тестерам, а ведущий программист по тому, будут модульные тесты или нет, исходя из поставленных задач.
Скажем, алгоритм быстрой сортировки быстрее и правильнее проверить тестом, нежели отладочным консольным приложением или, что ещё хуже, в реальном приложении (убедиться, что всё будет хорошо в программе, стоит конечно, но не отлаживать таким образом).
Скажем, алгоритм быстрой сортировки быстрее и правильнее проверить тестом, нежели отладочным консольным приложением или, что ещё хуже, в реальном приложении (убедиться, что всё будет хорошо в программе, стоит конечно, но не отлаживать таким образом).
да можно выйти раньше конкурентов скажем на неделю, а потом отгрести от пользователей за баги, недоделки и тд и тп. Я думаю что лучше выпустить софт позже на наделю, но качественней чем у конкурентов. Ну а если конкуренты вышли на неделю раньше с тем же качеством что и у вас, то тут стоит задуматься вообще об эффективности работы компании
Это бизнес решения. Да, они должны приниматься осознанно, но ситуация когда надо выйти с версией пораньше, сделать пресс рилиз о новой функциональности во время гораздо важнее того качества, которое получят пользователи.
Печально? Да.
Но это реальности
Печально? Да.
Но это реальности
UFO just landed and posted this here
Я не мою руки каждый раз перед едой, потому что…
«Я не пишу юнит-тесты, потому что ...» хочу потерять работу.
В моей компании, это так.
В моей компании, это так.
Ну и давайте рассмотрим временные потери на TDD:
Вот у меня был программист. В общем-то неплохой профессионал. Даёшь техническое задание — пишет код. Никаких юнит-тестов, никакого continuous integration. Тестирование модульное — интеграционные тесты, регрессионные тесты. Написание кода занимает около 30%, 70% — итеративная доработка по регрессам.
Техническое задание склонно к мутация ибо бизнес-задачи. В случае изменения параметров ТЗ фильтруем список для регрессионного тестирования, шерстим интеграционные тесты. Трудозатраты: 1 программист, 1 тестировщик, 1 продакт.
Попробуем ввести TDD. Выясняется, что ТЗ нужно вначале декомпозировать на требования, понятные программисту, написать хотя бы подробную спецификацию продукта, иначе непонятно, как писать функциональные требования. Далее выясняется, что перед тем, как писать тесты неплохо бы описать архитектуру раза в два точнее. Ок. Вводим роль архитектора, который следит за мутацией требований и поддерживает консистентность спецификаций. Кто у нас будет архитектором? Естественно — самый мудрый дядька. На каждый модуль начинаем писать тесты. Написали, посмотрели — слишком много параметров. Надо декомпозировать. Декомпозировали — слишком много тестов. Кого садим писать тесты? Джуниора. Посадили, написали. Начали кодить. Требования дополнились. Спецификации поменялись, тесты поменялись, код рефакторим. Написали, начали регрессионное тестирование. Обнаруживаем баги — чёрт! — багина спецификацию. Значит надо тестировать спецификацию. Кто будет тестирвоать спецификацию? Тест аналитик. Вводим роль. Наконец-то полностью прокатываем процесс, после чего видим, что регрессионное тестирование опять таки занимает 70% времени.
Подбиваем трудозатраты:
0.3 архитектора, 0.1 тест аналитика, 0.7 джуниора, 1 программист, 1 тестировщик
Время проекта выросло на 40% (потому что добавились точки синхронизации архитектор-аналитик, аналитик-тестировщик и архитектор-программист).
Затраты на проект выросли на 60% (роль архитектора и аналитика отнюдь не бесплатные).
К чему я это всё: я не против ТДД, но надо знать, где его применять. В проектах с низкой мутацией требовании элементы TDD работают и работают неплохо (если не увлекаться слишком сильной детализацией архитектуры), но в проектах с высокой скоростью изменения требований TDD просто неэффективен.
Сам я требую покрывать программистам только публичные интерфейсы модулей системы и писать автоматизированные интеграционные тесты. Разрабатывать весь код по TDD считаю слишком большим оверхедом.
Вот у меня был программист. В общем-то неплохой профессионал. Даёшь техническое задание — пишет код. Никаких юнит-тестов, никакого continuous integration. Тестирование модульное — интеграционные тесты, регрессионные тесты. Написание кода занимает около 30%, 70% — итеративная доработка по регрессам.
Техническое задание склонно к мутация ибо бизнес-задачи. В случае изменения параметров ТЗ фильтруем список для регрессионного тестирования, шерстим интеграционные тесты. Трудозатраты: 1 программист, 1 тестировщик, 1 продакт.
Попробуем ввести TDD. Выясняется, что ТЗ нужно вначале декомпозировать на требования, понятные программисту, написать хотя бы подробную спецификацию продукта, иначе непонятно, как писать функциональные требования. Далее выясняется, что перед тем, как писать тесты неплохо бы описать архитектуру раза в два точнее. Ок. Вводим роль архитектора, который следит за мутацией требований и поддерживает консистентность спецификаций. Кто у нас будет архитектором? Естественно — самый мудрый дядька. На каждый модуль начинаем писать тесты. Написали, посмотрели — слишком много параметров. Надо декомпозировать. Декомпозировали — слишком много тестов. Кого садим писать тесты? Джуниора. Посадили, написали. Начали кодить. Требования дополнились. Спецификации поменялись, тесты поменялись, код рефакторим. Написали, начали регрессионное тестирование. Обнаруживаем баги — чёрт! — багина спецификацию. Значит надо тестировать спецификацию. Кто будет тестирвоать спецификацию? Тест аналитик. Вводим роль. Наконец-то полностью прокатываем процесс, после чего видим, что регрессионное тестирование опять таки занимает 70% времени.
Подбиваем трудозатраты:
0.3 архитектора, 0.1 тест аналитика, 0.7 джуниора, 1 программист, 1 тестировщик
Время проекта выросло на 40% (потому что добавились точки синхронизации архитектор-аналитик, аналитик-тестировщик и архитектор-программист).
Затраты на проект выросли на 60% (роль архитектора и аналитика отнюдь не бесплатные).
К чему я это всё: я не против ТДД, но надо знать, где его применять. В проектах с низкой мутацией требовании элементы TDD работают и работают неплохо (если не увлекаться слишком сильной детализацией архитектуры), но в проектах с высокой скоростью изменения требований TDD просто неэффективен.
Сам я требую покрывать программистам только публичные интерфейсы модулей системы и писать автоматизированные интеграционные тесты. Разрабатывать весь код по TDD считаю слишком большим оверхедом.
И, да, я видел несколько проектов в крупных компаниях, где TDD был эффективен. Впрочем, после этих компаний у меня сложилось впечатление, что чистый TDD может применяться только вместе с RUP или Waterfall.
Константин, а как «непонятно, как писать функциональные требования» связано с написанием юнит-тестов? Речь идёт именно про юнит-тесты, не о функциональном, ни о приёмочном, где нужны юзер-стори. Юнит-тестинг напрямую не зависит ни от требований заказчика, ни от подробности ТЗ. Если программист пишет какой-то модуль либо дополняет его либо изменяет методы какого-то класса — просто пишутся/меняются соответствующие юнит-тесты, и архитектор тут совершенно ни при чём. «баги на спецификацию» — опять же, причём тут юнит-тесты? В основе, они тестируют модульные составляющие, на то они и «юнит». Ну а если меняется что-то внутри «модулей» системы, то тогда и тесты меняются соответственно, а иначе зачем они. Аналитики, архитекторы и др. доп роли и их отсутствие никак не должно влиять на наличие юнит-тестов программистов, потому что код пишется ими же и юнит-тесты ими же тоже. За общее функционирование системы отвечают функциональные/приёмочные тесты — они да, зависят от спецификации и юзер-стори заказчика.
Николай, объясни тогда, пожалуйста, каким откуда у программиста берётся время на написание юнит тестов? Мне кажется, что просто часть своего рабочего времени он эти юнит тесты пишет.
А теперь, ответив на этот вопрос, давай подумаем: вот есть программист. Без юнит тестов он тратит на разработку N своего времени. Если он начинает писать юнит тесты, то времени он потратит N*k, k при этом явно существенно больше единицы. Таким образом для юнит тестирования мы должны гораздо больше дорогостоящего времени программиста.
Если посадить рядышком архитектора, уточняющего функциональные требования, то k уменьшится. А можно и вообще свести k к единице, переложив функцию написания юнит тестов на куда как менее дорогостоящего джуниора. Архитектор и джуниор становятся экономически оправданными при з.п. в 1.5 и 1/3 по отношению к з.п. программера. Если не нанимать архитектора и джуниора программер по ТДД уже обходится существенно дороже.
Следующий вопрос: нет ли выигрыша при использовании ТДД по времени разработки за счёт времени на тестирование? Ответ НЕТ! Не выигрывает. Опыт меня и моих коллег показывает, что как надо было тратить 50-70% времени на неавтоматизируемые регрессы, так и с внедрением континиоусинтегрейшн продолжаем тратить столько же времени. Вопрос — почему? Ответ — гетерогенная среда и высокая мутабельность требований к программе.
Преимущество ТДД не в скорости или тоимости разработки, а в лучшем контроле качества кода в итоге, но не более того. ТДД — это не Святой Грааль и внедрять его там, где он не нужен — это проигрывать гонку с затратами на разработку.
А теперь, ответив на этот вопрос, давай подумаем: вот есть программист. Без юнит тестов он тратит на разработку N своего времени. Если он начинает писать юнит тесты, то времени он потратит N*k, k при этом явно существенно больше единицы. Таким образом для юнит тестирования мы должны гораздо больше дорогостоящего времени программиста.
Если посадить рядышком архитектора, уточняющего функциональные требования, то k уменьшится. А можно и вообще свести k к единице, переложив функцию написания юнит тестов на куда как менее дорогостоящего джуниора. Архитектор и джуниор становятся экономически оправданными при з.п. в 1.5 и 1/3 по отношению к з.п. программера. Если не нанимать архитектора и джуниора программер по ТДД уже обходится существенно дороже.
Следующий вопрос: нет ли выигрыша при использовании ТДД по времени разработки за счёт времени на тестирование? Ответ НЕТ! Не выигрывает. Опыт меня и моих коллег показывает, что как надо было тратить 50-70% времени на неавтоматизируемые регрессы, так и с внедрением континиоусинтегрейшн продолжаем тратить столько же времени. Вопрос — почему? Ответ — гетерогенная среда и высокая мутабельность требований к программе.
Преимущество ТДД не в скорости или тоимости разработки, а в лучшем контроле качества кода в итоге, но не более того. ТДД — это не Святой Грааль и внедрять его там, где он не нужен — это проигрывать гонку с затратами на разработку.
Постараюсь в порядке очереди вопросов.
Для конкретизации, предположим, что существуют условия, при которых программист (предпочитаю понятие «разработчик»), имеет область задач, где определённые не запрещённые требования могут выполняться. Например, идеальная схема Заказчик --> PM --> Tech Lead --> engineer. При других выдуманных схемах, не входящих ни в одну из утвержденных методологий, смысла говорить о «правилах» разработки, считаю, нет.
а) Откуда время у программиста.
По известным agile-методологиям существуют специальные мероприятия (например, планирование итерации), на которых непосредственные разработчики оценивают трудозатраты предстоящих задач (например, в очках или часах). Если заказчик/PM/кто-то-другой в прямой форме даёт понять, что «время ограничено только непосредственной разработкой кода» — вопрос не имеет места. Но оценка задач при выбранной методологии должна включать наличие написания тестов, если таковые имеются.
b) Безусловно, программист (разработчик) тратит больше времени на код и тесты для кода, чем на просто-код, к гадалке не ходи. «Дорогостоящее» время разработчика — его цена коррелирует с базовым ТЗ заказчика. Например, заказчик явно указал: «Я не готов платить за дополнительный % времени на написание тестов. Пожалуйста, пишите код без тестов — приоритет на общем состоянии проекта без учёта 100% безошибочной функциональности, пусть бажно, если есть баги. Опять же, тут вопрос посвещённости заказчика в суть проблемы и принятия им решения на основе последствий — не проблема программиста при наличие PM.
c) Архитектор, безусловно, может уменьшить функциональные требования, ибо функциональность заложена в самом понятии „функциональности“. Но ни один архитектор в здравом уме не будет писать юнит-тесты ни за одного программиста — иначе это не архитектор. Перекладывать функцию юнит-тестов на джуниоров… честно говоря, я не знаком ни с одним agile-подходом, который бы освобождал разработчиков от написания тестов и перекладывания на джуниоров.
d) Не нужно путать разработчика (junior || seniour) с тестировщиком. Одно дело — юнит-тесты на код, представляющий собой чёрный ящик, другое дело — функциональное/приёмочное тестирование на основе юзер-стора заказчика, существующее независимо от кода. Если оно не нужно заказчику — опять же, ответ не имеет смысла.
И да, согласен, преимущества TDD абсолютно не в абсолютной скорости разработки кода — никогда скорость в абсолютном значении не была выше только потому, что на разработку тратилось больше времени. И уж тем более не стоимость, ты прав — чем выше качество, тем дороже.
ЗЫ:
Любые комментарии нужно понимать правильно, как бы неверными они не воспринимались.
TDD — не де-факто для любого продукта, включая веб-проекты. Вполне возможно написание относительно качественного проекта без TDD. И нет предела совершенству, но страховка всегда существует и стоит дополнительного чего-либо.
Юнит-тесты — не определяют методологию TDD: TDD включает в себя не только понятия, по и последовательность действий и лояльное осознанное отношение к самому процессу.
Про мутабельность и пр. — это, имхо, отдельная тема. Основной вопрос — выбрать TDD в ущерб чему-то или нет. И другой вопрос — тратить время хотя бы на unit-tests или нет.
Для конкретизации, предположим, что существуют условия, при которых программист (предпочитаю понятие «разработчик»), имеет область задач, где определённые не запрещённые требования могут выполняться. Например, идеальная схема Заказчик --> PM --> Tech Lead --> engineer. При других выдуманных схемах, не входящих ни в одну из утвержденных методологий, смысла говорить о «правилах» разработки, считаю, нет.
а) Откуда время у программиста.
По известным agile-методологиям существуют специальные мероприятия (например, планирование итерации), на которых непосредственные разработчики оценивают трудозатраты предстоящих задач (например, в очках или часах). Если заказчик/PM/кто-то-другой в прямой форме даёт понять, что «время ограничено только непосредственной разработкой кода» — вопрос не имеет места. Но оценка задач при выбранной методологии должна включать наличие написания тестов, если таковые имеются.
b) Безусловно, программист (разработчик) тратит больше времени на код и тесты для кода, чем на просто-код, к гадалке не ходи. «Дорогостоящее» время разработчика — его цена коррелирует с базовым ТЗ заказчика. Например, заказчик явно указал: «Я не готов платить за дополнительный % времени на написание тестов. Пожалуйста, пишите код без тестов — приоритет на общем состоянии проекта без учёта 100% безошибочной функциональности, пусть бажно, если есть баги. Опять же, тут вопрос посвещённости заказчика в суть проблемы и принятия им решения на основе последствий — не проблема программиста при наличие PM.
c) Архитектор, безусловно, может уменьшить функциональные требования, ибо функциональность заложена в самом понятии „функциональности“. Но ни один архитектор в здравом уме не будет писать юнит-тесты ни за одного программиста — иначе это не архитектор. Перекладывать функцию юнит-тестов на джуниоров… честно говоря, я не знаком ни с одним agile-подходом, который бы освобождал разработчиков от написания тестов и перекладывания на джуниоров.
d) Не нужно путать разработчика (junior || seniour) с тестировщиком. Одно дело — юнит-тесты на код, представляющий собой чёрный ящик, другое дело — функциональное/приёмочное тестирование на основе юзер-стора заказчика, существующее независимо от кода. Если оно не нужно заказчику — опять же, ответ не имеет смысла.
И да, согласен, преимущества TDD абсолютно не в абсолютной скорости разработки кода — никогда скорость в абсолютном значении не была выше только потому, что на разработку тратилось больше времени. И уж тем более не стоимость, ты прав — чем выше качество, тем дороже.
ЗЫ:
Любые комментарии нужно понимать правильно, как бы неверными они не воспринимались.
TDD — не де-факто для любого продукта, включая веб-проекты. Вполне возможно написание относительно качественного проекта без TDD. И нет предела совершенству, но страховка всегда существует и стоит дополнительного чего-либо.
Юнит-тесты — не определяют методологию TDD: TDD включает в себя не только понятия, по и последовательность действий и лояльное осознанное отношение к самому процессу.
Про мутабельность и пр. — это, имхо, отдельная тема. Основной вопрос — выбрать TDD в ущерб чему-то или нет. И другой вопрос — тратить время хотя бы на unit-tests или нет.
«И другой вопрос — тратить время хотя бы на unit-tests или нет.»
Коль, моя позиция проста: тратить время на юнит тесты необходимо, но не везде. Юнит тестами надо покрывать внешние интерфейсы атомарных модулей. Критерий атомарности модуля —
а. код, который может по требованиям написать средний программист компании с 0 за неделю
б. критически важный для системы сегмент, вынесенный по этим причинам в отдельный компонент
Коль, моя позиция проста: тратить время на юнит тесты необходимо, но не везде. Юнит тестами надо покрывать внешние интерфейсы атомарных модулей. Критерий атомарности модуля —
а. код, который может по требованиям написать средний программист компании с 0 за неделю
б. критически важный для системы сегмент, вынесенный по этим причинам в отдельный компонент
theOnlyBoy расписал все в точности, что я хотел сказать
С чего вы взяли, что при использовании TDD вам нужен в довесок архитектор + тестер + бизнес аналитик?
Так же ваше заявление, что на проектах, где меняются бизнес требования часто, TDD неприемлим, из моего опыта, это абсолютная неправда. Если проект следует TDD, все и вся покрыто тестами, то когда приходит новый реквест поменять функционал, меняется тест, меняется код + все это ранится и у разработчика, который сделал изменения есть уже 99% уверенность, что все работает. В убер трупер проектах, без тестов, разработчик как на минном поле, он должен сто раз перепроверить эффект своего изменения, потратить как вы сказали 30% на код и 70% на верификацию, и еще потом находится в сомнениях. При TDD разработчик тратит 50% на код, 50% на тесты (это если с нуля), если надо поменять код, то 80% на код и 20% на тест.
С чего вы взяли, что при использовании TDD вам нужен в довесок архитектор + тестер + бизнес аналитик?
Так же ваше заявление, что на проектах, где меняются бизнес требования часто, TDD неприемлим, из моего опыта, это абсолютная неправда. Если проект следует TDD, все и вся покрыто тестами, то когда приходит новый реквест поменять функционал, меняется тест, меняется код + все это ранится и у разработчика, который сделал изменения есть уже 99% уверенность, что все работает. В убер трупер проектах, без тестов, разработчик как на минном поле, он должен сто раз перепроверить эффект своего изменения, потратить как вы сказали 30% на код и 70% на верификацию, и еще потом находится в сомнениях. При TDD разработчик тратит 50% на код, 50% на тесты (это если с нуля), если надо поменять код, то 80% на код и 20% на тест.
Прочитайте пожалуйста: habrahabr.ru/blogs/tdd/112685/#comment_3611572
ТДД далеко не всегда экономически оправдано. Вы рассуждаете, как программист, я рассуждаю, как человек, который платит программистам зарплату из своего кармана.
ТДД далеко не всегда экономически оправдано. Вы рассуждаете, как программист, я рассуждаю, как человек, который платит программистам зарплату из своего кармана.
Зато делать говно экономически оправдано gamedev.by/blog/Victor/%D0%9A%D0%BE%D0%BF%D1%80%D0%BE%D1%8D%D0%BA%D0%BE%D0%BD%D0%BE%D0%BC%D0%B8%D0%BA%D0%B0
Сколько, если не секрет? ;)
Вы лукавите как минимум.
Получается, что при первом подходе программист начинает кодировать на основании НЕПОНЯТНЫХ ЕМУ требований?..
Получается, что при первом подходе программист начинает кодировать на основании НЕПОНЯТНЫХ ЕМУ требований?..
Да, именно так. Хороший программист вполне способен сам уточнить требования у продакт-менеджера, для этого продакт и нужен, чтобы быть медиатором между бизнес задачами и задачами разрабоки.
Ну так и при написании юнит-тестов пусть он делает то же самое без всех этих введений архитекторов и аналитиков.
я отвечал выше в этом топике: habrahabr.ru/blogs/tdd/112685/#comment_3611572
Сколько процентов времени он тратит непосредственно на кодинг, а сколько на уточнение задач?
Сильно зависит от скорости мутации требований. Обычно на обсуждение функционала выделяется по 1-1,5 часа два раза в неделю при эффективном рабочем времени в 30 рабочих часов в неделю.
а сколько времени выделяется на проверку результата, кто и как этим занимается?
Функционал проверяется тестировщиком, который вначале с продактом пишет/уточняет тест-план, а потом составляет список регресс-тестов.
Время на прохождение тестов и правку багов обычно соотносится как 2 к 1 по отношению к времени разработки. Увеличение до 7 к 3 является поводом для тотального код-ревью.
Время на прохождение тестов и правку багов обычно соотносится как 2 к 1 по отношению к времени разработки. Увеличение до 7 к 3 является поводом для тотального код-ревью.
Что-то не вижу связи между архитектором и TDD, в любом случае даже средний разработчик без TDD и архитектора будет декомпозировать задачу на модули, а покрыть их хотя бы базовыми тестами после освоения самой технологии тестирования не займёт много времени, а дальше уже тесты будут пополняться в процессе.
А выигрыш от минимизации отладки скорее всего покроет потери от написания тестов.
Изменения требования конечно создают проблему, но без тестов проблем будет больше — служба поддержи нужна будет больше
А выигрыш от минимизации отладки скорее всего покроет потери от написания тестов.
Изменения требования конечно создают проблему, но без тестов проблем будет больше — служба поддержи нужна будет больше
Прочитайте пожалуйста: habrahabr.ru/blogs/tdd/112685/#comment_3611572
Начал читать «Совершенный код» Макконнелла, так вот он пишет что масса исследований подтверждает что чем раньше найдена ошибка тем дешевле её исправить и цена возрастает на порядок, т.е. увеличени стоимости разработки должно компенсироваться уменьшением стоимость правки багов.
Имею свежий пример когда нашёл не совсем очевидный баг просто собравшись писать тест, код только написал, он весь в памяти, поэтому только подумав о тесте, я понял где может быть проблема, уверен что маловероятно обнаружения этого бага при тестировании, а если бы возник на продакшене, пришлось бы всё дебажить — код бы подзабылся.
Ну и отладка при наличии тестов должна быть короче.
Касательно, откуда время я ответил изначально, это выбор разработчика, в одинаковых условиях один находит время, и при этом никак не страдает, а считается экспертом
Имею свежий пример когда нашёл не совсем очевидный баг просто собравшись писать тест, код только написал, он весь в памяти, поэтому только подумав о тесте, я понял где может быть проблема, уверен что маловероятно обнаружения этого бага при тестировании, а если бы возник на продакшене, пришлось бы всё дебажить — код бы подзабылся.
Ну и отладка при наличии тестов должна быть короче.
Касательно, откуда время я ответил изначально, это выбор разработчика, в одинаковых условиях один находит время, и при этом никак не страдает, а считается экспертом
я имею ввиду комент habrahabr.ru/blogs/tdd/112685/#comment_3611544
Это, конечно, да. Я и не отрицаю необходимости написания юнит-тестов. Но не всегда. Есть ситуации, когда это просто экономически невыгодно.
Слышал часто: "… потому что нет времени на это" и «а нафига, у нас есть тестеры, которые проверяют».
Очень хорошо, что появилась такая заметка. В комментариях к последней статье меня, возможно, обоснованно, критиковали за то, что я очень мало внимания уделяю автоматизированному тестированию.
Наверное так и есть: когда пишу код, то задумываю схему тестирования не так уж часто, и это при том, что тестирование встроено в «родной» фреймворк.
В чём моя отговорка? В том, что мне чаще всего трудно формализовать те требования к коду, которые я не мог бы отладить в голове. Грубо говоря, трудно формализовать требования к работе сцеплений, если это не какая-то явная ошибка, а совпадение событий, о котором поначалу даже и не думаешь.
Например, есть класс, который как-то работает с ГУИ, взаимодействует с массой других объектов, внутренних и внешних, часть из которых обрабатывает данные в отдельных потоках. Проблема «перемалывания» известных входных данных этим классом, чаще всего, для меня не стоит (потому что класс либо простой, либо для него был создан и протестирован небольшой этюд). Реальные проблемы проявляются в runtime: внутренние объекты возвращают специфические состояния, каждое из которых не является ошибочным, но вместе это так влияет на сообщения класса внешним объектам, что иногда может произойти та или иная логическая ошибка. То есть цепочка «если ...» настолько велика и заранее непредсказуема, что я не представляю как заранее создать такую ситуацию. Как правило, о таких ошибках сцепки классов поначалу даже не задумываешься.
Наверное так и есть: когда пишу код, то задумываю схему тестирования не так уж часто, и это при том, что тестирование встроено в «родной» фреймворк.
В чём моя отговорка? В том, что мне чаще всего трудно формализовать те требования к коду, которые я не мог бы отладить в голове. Грубо говоря, трудно формализовать требования к работе сцеплений, если это не какая-то явная ошибка, а совпадение событий, о котором поначалу даже и не думаешь.
Например, есть класс, который как-то работает с ГУИ, взаимодействует с массой других объектов, внутренних и внешних, часть из которых обрабатывает данные в отдельных потоках. Проблема «перемалывания» известных входных данных этим классом, чаще всего, для меня не стоит (потому что класс либо простой, либо для него был создан и протестирован небольшой этюд). Реальные проблемы проявляются в runtime: внутренние объекты возвращают специфические состояния, каждое из которых не является ошибочным, но вместе это так влияет на сообщения класса внешним объектам, что иногда может произойти та или иная логическая ошибка. То есть цепочка «если ...» настолько велика и заранее непредсказуема, что я не представляю как заранее создать такую ситуацию. Как правило, о таких ошибках сцепки классов поначалу даже не задумываешься.
Два примера проблем с юнит-тестами:
— Разработка ПО для синтеза звука. Типичный результат багов — дефекты звучания, призвуки. Для тестирования нужны уши и мозг, создание AI для тестирования проблематично.
— Мобильная разработка — есть несколько десятков устройств различных производителей, железа, осей, API, и т.д.
При этом в обоих случаях есть и автотестируемые регрессии, но за весь цикл разработки проекта они портят всего несколько человеко-дней, в то время как не-автотестируемые — причиняют очень неслабый объём работы.
— Разработка ПО для синтеза звука. Типичный результат багов — дефекты звучания, призвуки. Для тестирования нужны уши и мозг, создание AI для тестирования проблематично.
— Мобильная разработка — есть несколько десятков устройств различных производителей, железа, осей, API, и т.д.
При этом в обоих случаях есть и автотестируемые регрессии, но за весь цикл разработки проекта они портят всего несколько человеко-дней, в то время как не-автотестируемые — причиняют очень неслабый объём работы.
Будет и на их улице праздник.
Ну как бы известно же, что юнит-тесты — это самый низкий уровень, они и не должен тестировать звуки, цвет, ну и вообще поведение системы в целом. Они должны тестировать отдельные функции. Ну например, есть же у вас функция, которая подсчитывает ряд фурье, интеграл и т.п. — вот её и надо юнит-тестировать.
UFO just landed and posted this here
ИМХО пропущен самый главный пункт:
я не пишу тесты, по тому, что не знаю как это делать, в институте не научили, английский я не выучил, а на русском книг нет. «А в Delphi/VB это в меню где что нажать надо?»
я не пишу тесты, по тому, что не знаю как это делать, в институте не научили, английский я не выучил, а на русском книг нет. «А в Delphi/VB это в меню где что нажать надо?»
Порадовался за свой универ — у нас на практике по ООП отрабатывали юнит-тестирование, на реализациях разных паттернов программирования. Было на самом деле удобно их реализовывать с помощью тестов. Там же узнал(и читал/читаю) из списка рекомендованной литературы про TDD(книга К.Бека) и про Б.Эккеля «Философия С++». И про паттерны.
Вчера разбирался как тесты к NetBeans 7.0b прикручиваются — вроде удобно. Собираюсь покрыть ими одну свою программку — посмотрим, что получится.
Вчера разбирался как тесты к NetBeans 7.0b прикручиваются — вроде удобно. Собираюсь покрыть ими одну свою программку — посмотрим, что получится.
Несколько странная статья. Первый абзац – автор говорит про TDD, потом вводная «Все мы слышали множество оправданий, почему кто-то не использует TDD», но весь остальной текст по сути к TDD относится постольку-посколько. По сути, речь идет о поводах не писать юнит тесты (на что намекает заголовок), а вовсе не про использование/не использование TDD методологии.
Вообще, подобным совмещением TDD и юнит тестов, пропагандисты TDD добиваются обратного результата. Человек копнет глубже в TDD, поймет что это такое, вздрогнет и больше не подойдет к юнит тестам вообще. И ничего странного, ведь все «знатоки» прямо отождествляют эти два, совершенно разноуровневых понятия.
Вообще, подобным совмещением TDD и юнит тестов, пропагандисты TDD добиваются обратного результата. Человек копнет глубже в TDD, поймет что это такое, вздрогнет и больше не подойдет к юнит тестам вообще. И ничего странного, ведь все «знатоки» прямо отождествляют эти два, совершенно разноуровневых понятия.
Я виню юношеский максимализм ;) «Если использовать юнит-тесты в принципе, то сразу в режиме ТДД».
Мне кажется, в контексте данной статьи разница между юнит-тестами и TDD совершенно не важна.
я не очень понимаю, какой в этой статье особенный контекст, который делает прямую и явную неграмотность автора текста настолько неважной. Может не неграмотность, но умышленное введения читателя в заблуждение, я не знаю. Да и контекст этот мне трудно определить. Это про что, вообще было? Про «Все мы слышали множество оправданий, почему кто-то не использует TDD» или все таки про «Я не пишу юнит тесты… »? Подобная мешанина встречается на каждом шагу и вносит в нестойкие головы тех, кто тесты еще не пишет твердое, но очень ошибочное определение — юнит тесты это TDD
Качество кода нужно повышать ровно до той отметки, пока затраты на улучшение качества ниже, чем потенциальное увеличение прибыли от повышения качества.
Это должен понимать каждый программист, чтобы не заниматься перфекционизмом в ущерб коммерческим интересам проекта.
Это должен понимать каждый программист, чтобы не заниматься перфекционизмом в ущерб коммерческим интересам проекта.
КО напоминает:
тесты != unit-тесты
TDD != unit-тесты
тесты != unit-тесты
TDD != unit-тесты
Я знаю, что такое юнит-тесты, я знаю, какую пользу они приносят. Но я не пишу юнит-тесты, потому что хотелки заказчика появляются как грибы после дождя — в случайных местах и не всегда понятно, какой именно гриб перед тобой. Несмотря на всю формальность процесса (SRS, CR, итерации и т.д.) получается примерно следующее:
хотим, чтобы шрифт здесь был 10 — описано в SRS — сделано — а нет, у нас названия сюда не влезают, хотим 11 — заведен CR — сделано — прошел месяц — не, ваще не хотим здесь ничего, пусть это появляется там и настраивается через визард — заведен CR, но что именно нужно настраивать через визард еще не знаем, нет спеки — делаем что-то — оно им (не)нравится, и так несколько раз — нашли, что нужно — под уже готовый(!) функционал делается запись в SRS. На последних этапах этого «процесса» уже даже CRы не заводятся, зачем, все равно всем понятно, что никому ничего не понятно.
Это уже смело можно назвать BDD — Bug Driven Development =) Заказчик выдвигает абстрактные требования без какой-либо конкретики — разработчик делает что-то, процесс повторяется через заведение багов в JIRA.
В этом процессе на поддержку уже существующих юнит-тестов уходит 80% времени. Просто чтобы они удовлетворяли актуальному видению заказчика. Все это приводит к весьма плачевным результатам — старые тесты тупо удаляются, а новые не пишутся. В последствии что-то менять — смерти подобно.
хотим, чтобы шрифт здесь был 10 — описано в SRS — сделано — а нет, у нас названия сюда не влезают, хотим 11 — заведен CR — сделано — прошел месяц — не, ваще не хотим здесь ничего, пусть это появляется там и настраивается через визард — заведен CR, но что именно нужно настраивать через визард еще не знаем, нет спеки — делаем что-то — оно им (не)нравится, и так несколько раз — нашли, что нужно — под уже готовый(!) функционал делается запись в SRS. На последних этапах этого «процесса» уже даже CRы не заводятся, зачем, все равно всем понятно, что никому ничего не понятно.
Это уже смело можно назвать BDD — Bug Driven Development =) Заказчик выдвигает абстрактные требования без какой-либо конкретики — разработчик делает что-то, процесс повторяется через заведение багов в JIRA.
В этом процессе на поддержку уже существующих юнит-тестов уходит 80% времени. Просто чтобы они удовлетворяли актуальному видению заказчика. Все это приводит к весьма плачевным результатам — старые тесты тупо удаляются, а новые не пишутся. В последствии что-то менять — смерти подобно.
А за что собственно минусуете? Вполне себе нормальная ситуация. Только если в статье всё плохо, потому что программист какой-то неправильный, то ситуация с правильным программистом и неадекватным менеджером существует и достаточно распостранена.
По-моему, тест, который тестирует размер шрифта — это немного странный тест. Как тут уже было сказано, тестировать надо бизнес-логику, т.е. вход-выход отдельных функций. При чём тут шрифты?..
По-моему это обычное дело, когда заказчик выдвигает абстрактные требования. У него есть какая-то проблема, он не знает как её решить (скажем, потому что не обладает нужными компетенциями), но очень хочет и для этого обращается к разработчику.
Естественно, что заказчик описывает проблему абстрактно: в меру собственного понимания, каких-то ощущений и эмоций. Задача разработчика как раз и состоит в том, чтобы выявить «паталогию», придумать решение и реализовать.
Когда разработчик, не разобравшись в проблеме и не придумав решение, бросается «кодить» — это клиника — такую ситуацию, думаю, можно не рассматривать.
Если ваш заказчик берет на себя функцию принятия технических решений (например, размер шрифта), это скорее всего означает, что в ваших отношениях с ним что-то не так. И вам нужно, в первую очередь, решить этот вопрос. Ведь заказчик менее компетентен в сфере разрботки, нежели вы, и его действия вполне ожидаемо будут неверными. (в таком случае, он тупо будет учиться, на собтсвенных ошибках, — которые скорее всего будут выдаваться за ваши).
Может быть он вам не доверяет? В этом случае постарайтесь доказать свою компетенцию, чтобы у него не было сомнений. А может быть вы сами снимаете с себя ответственность за выработку решений? Тогда естественно эту функцию он будет брать на себя и станет выдумывать решение сам (ведь больше не кому).
Естественно, что заказчик описывает проблему абстрактно: в меру собственного понимания, каких-то ощущений и эмоций. Задача разработчика как раз и состоит в том, чтобы выявить «паталогию», придумать решение и реализовать.
Когда разработчик, не разобравшись в проблеме и не придумав решение, бросается «кодить» — это клиника — такую ситуацию, думаю, можно не рассматривать.
Если ваш заказчик берет на себя функцию принятия технических решений (например, размер шрифта), это скорее всего означает, что в ваших отношениях с ним что-то не так. И вам нужно, в первую очередь, решить этот вопрос. Ведь заказчик менее компетентен в сфере разрботки, нежели вы, и его действия вполне ожидаемо будут неверными. (в таком случае, он тупо будет учиться, на собтсвенных ошибках, — которые скорее всего будут выдаваться за ваши).
Может быть он вам не доверяет? В этом случае постарайтесь доказать свою компетенцию, чтобы у него не было сомнений. А может быть вы сами снимаете с себя ответственность за выработку решений? Тогда естественно эту функцию он будет брать на себя и станет выдумывать решение сам (ведь больше не кому).
Из личного опыта могу сказать точно, что при работе на больших и долгосрочных проектах Unit-тестирование — это лучший способ тестирования, при этом необходимость традиционных способов тестирования не отпадает. Отсюда следует, что отговорка «Нет времени» — это самая дешевая отговорка, т.к. на начальном этапе Unit-тестирование действительно требует чуть больше времени, но уже через 3-4 месяца, когда проект становится больше и сложнее, все время потраченное на тестирование полностью компенсируется на ускорении внедрения нового функционала и отлова багов в уже существующем.
Также существует проблема того, что не все заказчики хотят «тратить время» на тесты, тут им уже надо объяснять все вышеописанное.
И самое главное чтобы не получилась ситуация: «Научи дурака молиться он лоб расшибет», важно понять, что нет необходимости тестировать 100% (этого и не получится). Тестировать надо бизенс-логику.
Также существует проблема того, что не все заказчики хотят «тратить время» на тесты, тут им уже надо объяснять все вышеописанное.
И самое главное чтобы не получилась ситуация: «Научи дурака молиться он лоб расшибет», важно понять, что нет необходимости тестировать 100% (этого и не получится). Тестировать надо бизенс-логику.
но уже через 3-4 месяца
оправдано ли применение TDD в случае 2-3 недельных проектов, которые гарантированно не будут поддерживаться разработчиком в будущем?
оправдано ли применение TDD в случае 2-3 недельных проектов, которые гарантированно не будут поддерживаться разработчиком в будущем?
Если Вы разрабатываете этот проект на базе каких-то инструментов то скорее всего эти инструменты уже будут покрыты тестами, все остальное уже Ваше личное дело, если Вы хотите гарантировано закрыть проект без косяков, тогда тесты будут нужны, т.к. в случае возникновения проблем на проекте заказчик скорее всего обратиться к Вам. Если же проект ну совсем простой и точно не будет дальше дорабатываться тогда думаю, что ничего страшного если тестов не будет.
Новый проект начинаю с тестов, но быстро забиваю на них, так как тесты разрастаются куда быстрее кода. Простой пример, функция с телом return new SomeClass() — нужно проверить, что что-то вообще возвращается, что возвращается объект, что этот объект инстанс SomeClass, что он правильно проинициализирован. Потом появляется необходимость ввести параметр в функцию, который тупо передаётся в конструктор, где тупо присваивается публичному свойству — приходится сначала изменять уже написанные тесты, а затем писать новые тесты на то, что хоть что-то сохраняется, что тип не изменился, что сохраняется то, что передаётся. Ладно, с этим справились, начинается самое интересное — используемый фреймворк поддерживает только паттерн ActiveRecord и объекты модели наследуется от базового класса, который инкапсулирует БД. На две строчки obj = SomeFactory::getNewInstance(className, params); obj.save() приходится писать ещё штук 20 тестов, проверяющих не только что объект пишется и читается то, что записалось, но и что корректно выкидываются любые эксепшены, которые могут возникнуть при работе с БД, начиная от невозможности подключения заканчивая нарушением целостности, и как раз на этом месте энтузиазм, как правило, иссякает.
Нет необходимости тестировать каждый класс-наследник ActiveRecord — достаточно протестировать сам ActiveRecord (save, delete, update, find и др. базовые вещи) на базе тестового Mock-объекта, и тем более каждый раз проверять а корректно ли произошло подключение к БД.
Библиотека работы с БД (если такая есть во фреймворке), также должна тестироваться один раз, в одном месте, в противном случае Вы тестируете уже протестированное, а это избыточность тестов, которая собственно Вам и портит настроение писать тесты.
Библиотека работы с БД (если такая есть во фреймворке), также должна тестироваться один раз, в одном месте, в противном случае Вы тестируете уже протестированное, а это избыточность тестов, которая собственно Вам и портит настроение писать тесты.
Ну тестирую я не сам ActiveRecord (тесты для него есть в фреймворке), а связь своего класса с таблицей в БД (что маппинг правильно настроен другими словами), что ограничения введенные в схему БД (уникальные индексы, внешние ключи, ограничения на размер данных и т. п.) правильно работают, что переопределенный save() хотя бы вызывает родительский save(), а не попал в «мёртвый код» и т. п. Про подключение я, каюсь, добавил для красного словца, но как-то так получается, что одна строчка кода вызывает необходимость писать чуть ли не десятки тестов. С DataMapper было бы, наверное, проще тестировать модель, но, увы, скаффолдинг в фреймворке работает только с ActiveRecord.
Есть опыт запуска двух больших Интернет-проектов на фреймворке также поддерживающем только ActiveRecord, сначала тоже тестировали все модели, свойства и методы, но потом, когда тесты начали превращаться чуть-ли не copypaste, т.к. все модели в основном однотипны, успешно забили на это дело и переключились исключительно на тестирование бизнес-логики, вот тогда дело и пошло веселее.
Я конечно не знаю Вашей ситуации, но есть подозрение, что частично такая модель применима, ну если нет, так нет.
Я конечно не знаю Вашей ситуации, но есть подозрение, что частично такая модель применима, ну если нет, так нет.
Грубо говоря, инициализируете объекты модели, вызываете какой-то метод, проверяете или его возврат, или изменение состояние объекта и на этом успокаиваетесь?
по-хорошему да, например возьмем тот же ActiveRecord на примере функции save
методоы getAnyproperty и setAnyProperty — явно не объявлены и обыгрываются при помощи magic-метода __call PHP.
Таким образом, нам достаточно проверить только один объект, а все остальные модели смысла нет, т.к. абсолютное большинство, я думаю, будет однотипным.
Таким образом, нам достаточно проверить только один объект, а все остальные модели смысла нет, т.к. абсолютное большинство, я думаю, будет однотипным.
class ActiveRecord {
public function save() {//do something}
}
class SomeClass extends ActiveRecord {....}
$obj = new SomeClass();
$obj->setSomeModelProperty(«value»);
$obj->save();
// Проверка сохранения, если ID есть, то сохранило
$this->assertTrue(!is_null($obj->getId()));
// Проверка установки значение через __call
$this->assertTrue($obj->getSomeProperty() == «value»);
public function save() {//do something}
}
class SomeClass extends ActiveRecord {....}
$obj = new SomeClass();
$obj->setSomeModelProperty(«value»);
$obj->save();
// Проверка сохранения, если ID есть, то сохранило
$this->assertTrue(!is_null($obj->getId()));
// Проверка установки значение через __call
$this->assertTrue($obj->getSomeProperty() == «value»);
Если сложно написать тест то с кодом что-то явно не так.
Даже если отвлечься от фреймворка, модели которого сильно связаны с классами ORM, то рассуждаю примерно так: нужен метод, возвращающий объект класса MyClass с одним свойством и конструктором с параметром — значением этого свойства. Начинаем TDD:
— пишем тест, что Fabric::getInstanceOfMyClass(5) возвращает не null
— запускаем — ошибка, метод не существует
— пишем метод function getInstanceOfMyClass() {}
— запускаем — ошибка, метод без параметров, а передано одно целое
— добавляем параметр в метод function getInstanceOfMyClass(param) {}
— запускаем — ок
— пишем тест, что метод возвращает вообще хоть что-то (не NULL)
— запускаем — ошибка
— добавляем return 1
— запускаем — ок
— пишем тест, что возвращается объект
— запускаем — ошибка
— исправляем return new SomeClass() — ок
— пишем тест, что метод возвращает MyClass
— запускаем — ошибка — класс не определен
— пишем класс class MyClass() {}, настраиваем его загрузку
— запускаем — ок
— пишем тест, что свойство prop проинициализировано (не NULL)
— запускаем — ошибка, свойства нет
— дописываем в класс public prop;
— запускаем — ошибка, NULL
— пишем конструктор function constructor__() { $this->prop = "" }
— запускаем — ok
— пишем тест, что свойство целое
— запускаем — ошибка
— изменяем конструктор $this->prop = 0
— запускаем — ok
— пишем тест, что свойство равно 5
— запускаем — ошибка
— переписываем конструктор function constructor__(val) { $this->prop = val }
— запускаем — ошибка, не передан параметр в конструктор
— исправляем return new MyClass() на return new MyClass(1)
…
Устал :) В общем ещё с десяток итераций и получим код
покрытый десятком тестов на каждый чих
По-моему перебор
— пишем тест, что Fabric::getInstanceOfMyClass(5) возвращает не null
— запускаем — ошибка, метод не существует
— пишем метод function getInstanceOfMyClass() {}
— запускаем — ошибка, метод без параметров, а передано одно целое
— добавляем параметр в метод function getInstanceOfMyClass(param) {}
— запускаем — ок
— пишем тест, что метод возвращает вообще хоть что-то (не NULL)
— запускаем — ошибка
— добавляем return 1
— запускаем — ок
— пишем тест, что возвращается объект
— запускаем — ошибка
— исправляем return new SomeClass() — ок
— пишем тест, что метод возвращает MyClass
— запускаем — ошибка — класс не определен
— пишем класс class MyClass() {}, настраиваем его загрузку
— запускаем — ок
— пишем тест, что свойство prop проинициализировано (не NULL)
— запускаем — ошибка, свойства нет
— дописываем в класс public prop;
— запускаем — ошибка, NULL
— пишем конструктор function constructor__() { $this->prop = "" }
— запускаем — ok
— пишем тест, что свойство целое
— запускаем — ошибка
— изменяем конструктор $this->prop = 0
— запускаем — ok
— пишем тест, что свойство равно 5
— запускаем — ошибка
— переписываем конструктор function constructor__(val) { $this->prop = val }
— запускаем — ошибка, не передан параметр в конструктор
— исправляем return new MyClass() на return new MyClass(1)
…
Устал :) В общем ещё с десяток итераций и получим код
...
class MyClass {
public prop;
function __construct($val) {
$this->prop = $val
}
}
...
function getInstanceOfMyClass($param) {
return new MyClass($param)
}
...
покрытый десятком тестов на каждый чих
По-моему перебор
нет
А что с кодом не так?
С кодом всё в порядке.
Плюс у вас есть тесты, которые подтверждают, что с этим кодом всё в порядке.
Плюс у вас есть тесты, которые подтверждают, что с этим кодом всё в порядке.
Но тесты писать сложно для такого простого кода :(
нет
Мне сложно, правда понял почему из коммента ниже — слишком мелкие итерации делаю.
Вот видишь, даже плохой опыт написания тестов полезен, так как он влияет на «мировоззрение».
Окей. Я отвечу развёрнутей. Из личного опыта а так же наблюдений за другими программистами.
Вот такие простые проверки на null, проверки граничных значений и другие элементарные действия входят в привычку через пару дней честного применения TDD.
После того, как первый раз тесты спасут от комита, который мог всё разломать, появляется уверенность в полезности тестов.
Ещё через какое-то время, размышления над новым кодом начинаются с вопроса «Как я буду это тестировать?». Ну а когда у тебя уже готов тест, сам код написать можно даже пьяным.
Вот такие простые проверки на null, проверки граничных значений и другие элементарные действия входят в привычку через пару дней честного применения TDD.
После того, как первый раз тесты спасут от комита, который мог всё разломать, появляется уверенность в полезности тестов.
Ещё через какое-то время, размышления над новым кодом начинаются с вопроса «Как я буду это тестировать?». Ну а когда у тебя уже готов тест, сам код написать можно даже пьяным.
Меня напрягает не сколько сами проверки делать, а обрамлять их сначала синтаксически (function testMethodReturnNotNull() {..}), потом логически (obj = Fabric::getInstance(); ). Этот-то пример тривиальный, да и инициализацию можно легко вынести в setUp() для остального десятка тестов, а вот настраивать несколько моков или стабов, которые для каждого теста почти одинаковы, но только почти, ради одного ассерта… :( Не рефакторингом же тестов заниматься, а десяток экранов тестов на полэкрана кода как-то в уныние вводит.
Перебор по первой, в TDD самый главный навок, это размер «итерации» — по первой если вы новичок в TDD вы либо будите делать настолько гранулированные шаги, что вы привели сверху, либо гигансткие прыжки. Но потом с опытом, вы найдете баланс и будите двигать с той скоростью, с которой вам комфортно и главно с которой вы уверены, что ваши изменения имеют под собой тесты.
TDD это не столько тест на каждый чих, а сколько понимание, насколько ты уверен в чихе и насколько гранилированно должен быть тест.
Лучшая книга, это конечно, от отца основателя — «Test-Driven Development» by Kent Beck. Там процес нахождения этого очень хорошо расписан.
TDD это не столько тест на каждый чих, а сколько понимание, насколько ты уверен в чихе и насколько гранилированно должен быть тест.
Лучшая книга, это конечно, от отца основателя — «Test-Driven Development» by Kent Beck. Там процес нахождения этого очень хорошо расписан.
нужен метод, возвращающий объект класса MyClass с одним свойством и конструктором с параметром — значением этого свойства.
…
Устал :) В общем ещё с десяток итераций и получим код
— как сделать шаг? — нужно поднять ногу и, сохраняя равновесие, перенести её на 1 милиметр вперед, затем еще на 1 миллиметр, потом еще на 1 миллиметр,… — фу, как сложно ходить! ;)
тест — это та же запись требований, только на языке программирования. у вас тут одно понятное предложение. описывайте в тесте, сразу (и только) то, что требуется. зачем вся эта чехорда?
Хм, как-то считал, что очевидные требования, которые не требуют формализации, типа что метод count должен возвращать число, а не строку или объект, тоже надо записывать в виде тестов. В языках типа C*/Java с этим проще, просто не скомпилится если несоответствие типов будет, а вот в языках где выражение 10+«10» даже предупреждения не выдаст, но при этом 10!==«10» могут быть трудноловимые баги
все известные мне тест-фреймоврки содержат асерты для сравнения значений с учетом типа. PhpUnit::assertSame(), например. либо напишите в асерте count() === 10 — если вернет не число, тоже увидите fail.
ps: для неформализованных требований вы вряд-ли сможете написать тест ;)
ps: для неформализованных требований вы вряд-ли сможете написать тест ;)
Формализую в процессе написания теста, то есть как-то так:
— нужен метод — пишу тест на существование метода
— метод должен что-то возвращать — пишу тест что возвращает не нуль
— метод должен возвращать объект — пишу тест что возвращает объект
— метод должен возвращать объект такого-то класса — …
и т. д.
Грубо говоря, сначала тест, потом одна синтаксическая конструкция, новый тест, следующая конструкция (или исправление фейковой)
— нужен метод — пишу тест на существование метода
— метод должен что-то возвращать — пишу тест что возвращает не нуль
— метод должен возвращать объект — пишу тест что возвращает объект
— метод должен возвращать объект такого-то класса — …
и т. д.
Грубо говоря, сначала тест, потом одна синтаксическая конструкция, новый тест, следующая конструкция (или исправление фейковой)
Надо сначала проверять что метод не является чайником.
ага, это поиск. вы сами выбираете скорость, с которой хотите приближаться к цели. будете идти мелкими шагами — выйграете в контроле, но проиграете в темпе. крупными шагами — наоборот — пойдете быстро, но можете «не вписаться в поворот» и пролететь мимо. со временем найдете оптимальный.
Так и есть, перебор.
На самом деле не надо писать юнит-тесты для каждого чиха. Вот тут есть простой пример: habrahabr.ru/blogs/tdd/112851/
На самом деле не надо писать юнит-тесты для каждого чиха. Вот тут есть простой пример: habrahabr.ru/blogs/tdd/112851/
Я не пишу юнит-тесты поскольку основа ПО — это GUI и реакция на нажатия кнопок+база данных.
Когда обсуждали введение юнит-тестов стало понятно, что БД можно заменить на мок-объекты, но трудоёмко (эмуляция сложной структуры БД — это не подарок), с интерфейсом — тоже можно что-то сделать. Но руководство не пошло на увеличение на 30-40% трудоёмкости задач.
PS Архитектурно юнит-тесты не были предусмотрены с начала проекта.
Когда обсуждали введение юнит-тестов стало понятно, что БД можно заменить на мок-объекты, но трудоёмко (эмуляция сложной структуры БД — это не подарок), с интерфейсом — тоже можно что-то сделать. Но руководство не пошло на увеличение на 30-40% трудоёмкости задач.
PS Архитектурно юнит-тесты не были предусмотрены с начала проекта.
юнти-тесты это модульное тестирование — для маленьких атомарных единиц функционала. ну не бывает таких здоровых модулей, чтобы сразу и gui и база данных. ;)
контроллер взаимодествует и с моделью (бд), и с вью (gui) :)
Контроллер — это архитектурное решение, изначально в проекте его не было.
Сейчас ввести наверное можно, но трудоемко.
Мы с главным программером оценили эту работу в 2-3 недели (проектирование+внедрение в одном нетривиальном классе+отладка+решение граблей+написание мок объектов).
2-3 недели начальство не утвердило, а делать «втихаря» — я прошлый раз лишился части з/п за такое.
Сейчас ввести наверное можно, но трудоемко.
Мы с главным программером оценили эту работу в 2-3 недели (проектирование+внедрение в одном нетривиальном классе+отладка+решение граблей+написание мок объектов).
2-3 недели начальство не утвердило, а делать «втихаря» — я прошлый раз лишился части з/п за такое.
Я не пишу юнит-тесты, потому что еще не уверовал в их магическую силу экономить мне деньги, время и нервы. Одно время загорелся, написал парочку. Потом понял, что код, который они тестируют, никогда не изменится, и больше не возвращался к этому.
Я пришел к автоматическим тестам очень просто — проанализировав беклог только что закончившейся очень нервной итерации. Проекту — год. Оказалось, что лишь 20% сделанных задач было новыми штуками, которые двигают нас вперед, а 80% задач стало разлинчного рода багофиксингом. Причем в конце итерации, котрый мы «отыгрывали» уже в дополнительное время, было такое ощущение, что всё вышло из под контроля и идет «в разнос» — когда каждая доделка приводит к появлению/выявлению еще пары новых багов — и только каким-то чудом оно в итоге «сошлось».
Самая главная проблема в юнит-тестах (для меня) — это поддержка их в актуальном состоянии. На всех моих проектах (продуктах) уж так получается, что либо мы выпускаем продукт, либо разоряемся, зато пишем юнит-тесты.
Лично я являюсь сторонником написания тестов, но меня всегда забавляли «менеджерские» фразы типа: «На самом деле вас ожидает гораздо больше разных временных потерь, если вы не пишите юнит-тестов». Ведь большинство людей здесь связаны с разработкой ПО и прекрасно понимают психологию программистов: если тестировщики найдут баг, то я его поправлю.
Так значит кто именно тратит больше времени? Я готов согласится, что выпуск релиза может удленниться, потому что возникает пинание фичи между тестировщиком и разработчиком, но… Во-первых, разработчику проще писать код без тестов, потому что это требует меньше интеллектуальных затрат, а во-вторых, его _личного_ времени уйдет меньше, потому что именно тестировщик будет придумывать сложные сценарии отлова багов. Программист при этом будет занят чем-то другим, и если тестировщик таки найдет багу, то программист достаточно быстро ее поправит и дело с концом. Т.е. для себя лично программист будет уверен, что справился с задачей быстро, хотя таких round-trip'ов может быть много и реально время на разработку фичу в целом будет затрачено больше.
Я думаю, что правильнее мотивировать разработчиков писать тесты не словами типа «вы будете меньше времени тратить на фичи» или что-то в этом духе, а тем, что вы будете говорить: С тестами ты напишишь реально классный код, потому что если ты можешь его покрыть тестами, значит ты классно развязал все зависимости и отлично спроектировал интерфейсы. А фразы про меньше времени оставьте на убеждение менеджеров, программистам они не нужны.
Так значит кто именно тратит больше времени? Я готов согласится, что выпуск релиза может удленниться, потому что возникает пинание фичи между тестировщиком и разработчиком, но… Во-первых, разработчику проще писать код без тестов, потому что это требует меньше интеллектуальных затрат, а во-вторых, его _личного_ времени уйдет меньше, потому что именно тестировщик будет придумывать сложные сценарии отлова багов. Программист при этом будет занят чем-то другим, и если тестировщик таки найдет багу, то программист достаточно быстро ее поправит и дело с концом. Т.е. для себя лично программист будет уверен, что справился с задачей быстро, хотя таких round-trip'ов может быть много и реально время на разработку фичу в целом будет затрачено больше.
Я думаю, что правильнее мотивировать разработчиков писать тесты не словами типа «вы будете меньше времени тратить на фичи» или что-то в этом духе, а тем, что вы будете говорить: С тестами ты напишишь реально классный код, потому что если ты можешь его покрыть тестами, значит ты классно развязал все зависимости и отлично спроектировал интерфейсы. А фразы про меньше времени оставьте на убеждение менеджеров, программистам они не нужны.
Я пробовал писать юнит-тесты, но они мне ни разу не помогли. Тратишь пару дней, пишешь полсотни тестов, запускаешь — все работает. А на следующий день тебе рассказывают, что твоя программа, падает, например, в таком случае:
1. Вот только на этом компьютере у Васи, а на остальных двух сотнях работает. У него там, правда, кондёры на материнке полететели, но программа ведь все-равно должна работать, правда?
2. Если в системе установлена «вот такая» библиотека.
3. Если нажать <последовательность из 30-ти хаотических клавиш>
и т.д.
И вот смотришь на это — тесты у тебя все работают, а таких тестов, которые нашли бы вышеуказнные ошибки и в природе нет (и не может быть). И толку с тех тестов?
Отдельным пунктом выносятся случаи тестирования программ, в которых:
-«надо чтобы визуально было красиво»
-«вы вот этому внешнему устройству дайте комманду и оно что-то сделает. наверное.»
-«самого девайса у нас сейчас нет, но вот вам эмулятор»
и т.д.
1. Вот только на этом компьютере у Васи, а на остальных двух сотнях работает. У него там, правда, кондёры на материнке полететели, но программа ведь все-равно должна работать, правда?
2. Если в системе установлена «вот такая» библиотека.
3. Если нажать <последовательность из 30-ти хаотических клавиш>
и т.д.
И вот смотришь на это — тесты у тебя все работают, а таких тестов, которые нашли бы вышеуказнные ошибки и в природе нет (и не может быть). И толку с тех тестов?
Отдельным пунктом выносятся случаи тестирования программ, в которых:
-«надо чтобы визуально было красиво»
-«вы вот этому внешнему устройству дайте комманду и оно что-то сделает. наверное.»
-«самого девайса у нас сейчас нет, но вот вам эмулятор»
и т.д.
«писать юнит-тесты» и «TDD» это разные вещи
первое — бесполезно
первое — бесполезно
Вот только на этом компьютере у Васи, а на остальных двух сотнях работает. У него там, правда, кондёры на материнке полететели, но программа ведь все-равно должна работать, правда?
что «правда»? требовние работать на уникальном компрьютере Васи с полетевшими кондерами было с самого начала? если да, то вы этот кейс должны были уже вусмерть затеститровать. а если же нет — это либо нарушение технических требований (оборудование должно быть исправно), либо новая фича, за отдельные деньги. при чем тут tdd? тесты — это не только юнит тесты.
Ещё есть вариант «Было бы чего серьёзное тестировать». А то приложение — три таблицы, два запроса.
Могу предложить еще одну отговорку, которая для меня имеет довольно существенно значение. Юнит тесты не эстетичны, их код всегда грязен. Заполнен моками и описаниями не того, что мы хотим проверить, а того как мы это проверяем. Ведь мы сначала долго и мучительно конструируем экосистему для объекта, а потом вызываем метод и несколько валидаций, а скорее всего примитивных ассертов. Я не видал такого и не умею соответственно делать так, чтобы юнит тесты читались бы прозрачно.
Я сам в своей практике пробовал несколько раз использовать как ТДД, так и юнит тестирование. Ни разу позитивный опыт не получил, при том функциональные и интеграционные тесты это совсем другая песня. Вот они дарят постоянную радость и действительно уверенность в своем коде. Я не понимаю идеологию юнит тестирования, подавляющее большинство ошибок возникают из-за того, что ты не знаешь, как должен вести твой код с внешними подсистемами. Собственно, багов при поддержке кода, когда код ведет себя не так как ты от него ожидаешь, их не так много и они легко ловятся и без системы юнит тестов.
Схожее у меня отношение и к комментариям, если код требует комментария, лучше переписать код. Комментированным должен быть заметный кусок функциональности, который можно использовать отдельно, не переключаясь на ньансы его пользования. Та же логика, если мы близки к конкретному коду. ни тестирование, ни комментарии не так уж нужны, код и так вполне прозрачен. А вот большие куски кода оформленные как более менее завершенный АПИ, вот они нуждаются как в тестах, так и какойто документации, комментариях итп.
Я сам в своей практике пробовал несколько раз использовать как ТДД, так и юнит тестирование. Ни разу позитивный опыт не получил, при том функциональные и интеграционные тесты это совсем другая песня. Вот они дарят постоянную радость и действительно уверенность в своем коде. Я не понимаю идеологию юнит тестирования, подавляющее большинство ошибок возникают из-за того, что ты не знаешь, как должен вести твой код с внешними подсистемами. Собственно, багов при поддержке кода, когда код ведет себя не так как ты от него ожидаешь, их не так много и они легко ловятся и без системы юнит тестов.
Схожее у меня отношение и к комментариям, если код требует комментария, лучше переписать код. Комментированным должен быть заметный кусок функциональности, который можно использовать отдельно, не переключаясь на ньансы его пользования. Та же логика, если мы близки к конкретному коду. ни тестирование, ни комментарии не так уж нужны, код и так вполне прозрачен. А вот большие куски кода оформленные как более менее завершенный АПИ, вот они нуждаются как в тестах, так и какойто документации, комментариях итп.
Если вы долго и мучительно конструируете экосистему для объекта, не значит ли, что объект зависит от слишком большого количества внешних dependencies? Мы стараемся разбивать такие объекты на несколько, но да, бывает, что код инициализации mock'ов строк на 20.
Я незнаю, но у меня просто дичайший пример из моей практики — абсолютно один и тот же проект. Сначало там было около 200 интеграционных тестов и около 100 юнит тестов. 25 тысяч строк кода. В конечном итоге, стоимость изменения кода и стоимость прикручивания интеграционных тестов для новых фич просто похранило этот проект.
Тот же самый проект, TDD way, кода на 45 тысяч строк, ~2800 юнит тестов, всего 30 интеграционных (там, где оно надо). Изменения, которые пытались сделать в старом варианте на протяжении нескольких месяцев, были произведены за две недели.
Я незнаю, но у меня просто дичайший пример из моей практики — абсолютно один и тот же проект. Сначало там было около 200 интеграционных тестов и около 100 юнит тестов. 25 тысяч строк кода. В конечном итоге, стоимость изменения кода и стоимость прикручивания интеграционных тестов для новых фич просто похранило этот проект.
Тот же самый проект, TDD way, кода на 45 тысяч строк, ~2800 юнит тестов, всего 30 интеграционных (там, где оно надо). Изменения, которые пытались сделать в старом варианте на протяжении нескольких месяцев, были произведены за две недели.
На моей (сугубо корпоративной) практике основные места, где лезут баги и с которыми стоит бороться в первую очередь.
1. Интеграция. Когда оборудование или чужой, внешний код ведет себя не так, как ожидает программист. Его код работал бы верно, если бы экосистема работала бы так, как ожидает программист.
2. Многопоточка. Без комментариев, везде где есть многопоточка, есть и труднейшие баги.
3. Неправильный анализ задачи, неполная постановка задачи итп.
90%+ проблем с кодом растет ногами отсюда.
Юнит тесты крайне полезны в случае какого-то узкого нетривиального места, если какое-то в место кода раз за разом вызывает недоумение, это хороший аргумент на покрытие юнит тестом. Либо, на переписывание его в более достойный вид.
Покрывать весь код юнит тестами, это как писать комментарии на геттеры. Есть любители, наверное, но мне вот неудобно. Я, как и все, безусловно очень позитивно отношусь к тестам в целом, но вот ТДД для меня это что-то непонятное. Хотя допускаю, что в каких-то других внешних условиях, может и применимое.
1. Интеграция. Когда оборудование или чужой, внешний код ведет себя не так, как ожидает программист. Его код работал бы верно, если бы экосистема работала бы так, как ожидает программист.
2. Многопоточка. Без комментариев, везде где есть многопоточка, есть и труднейшие баги.
3. Неправильный анализ задачи, неполная постановка задачи итп.
90%+ проблем с кодом растет ногами отсюда.
Юнит тесты крайне полезны в случае какого-то узкого нетривиального места, если какое-то в место кода раз за разом вызывает недоумение, это хороший аргумент на покрытие юнит тестом. Либо, на переписывание его в более достойный вид.
Покрывать весь код юнит тестами, это как писать комментарии на геттеры. Есть любители, наверное, но мне вот неудобно. Я, как и все, безусловно очень позитивно отношусь к тестам в целом, но вот ТДД для меня это что-то непонятное. Хотя допускаю, что в каких-то других внешних условиях, может и применимое.
Статью можно перестать читать после слов «Я глубоко верю в методику TDD». Очередной верующий рассказывает про очередной silver bullet.
Никто в здравом уме не будет спорить с тем, что тесты иногда бывают очень полезны. Но говорить о том, что тесты (а тем более TDD) нужны абсолютно всегда — идиотизм. Самое ужасное, что как и любому другому верующему, доказать ошибочность его позиции невозможно. Покажешь ему: «смотри, вот делали по TDD, а получилось хуже чем у конкурентов». А в ответ: «это было какое-то неправильное TDD.ALWAYS TDD! TDD ONLY! NO THINKING REQUIRED! I LOVE TDD! TDD! TDD!».
Никто в здравом уме не будет спорить с тем, что тесты иногда бывают очень полезны. Но говорить о том, что тесты (а тем более TDD) нужны абсолютно всегда — идиотизм. Самое ужасное, что как и любому другому верующему, доказать ошибочность его позиции невозможно. Покажешь ему: «смотри, вот делали по TDD, а получилось хуже чем у конкурентов». А в ответ: «это было какое-то неправильное TDD.
Где в статье говорится, что TDD это silver bullet?
По-моему тесты не нужны, когда не предполагается внесение изменений в код или трудозатраты на них никого не волнуют. Тесты полезны, когда изменения вносятся редко и они глобальны. Тесты необходимы, если вся разработка фактически сплошное изменение :)
*не глобальны
Да можно 100500 раз жать F5 и писать die(var_export($val)); или 1 раз это автоматизировать.
Есть xdebug — достаточно жать F5 в IDE :)
В IDE можно жать и на Run Test Siute.
Можно, никто же не спорит, но поставить точку останова проще, чем написать тест из десятка строк. TDD и «классическое» программирование — две методики для достижения одного и того же, в одних ситуациях лучше одна, в других другая. Имхо, «классическое» программирование подходит больше для «академических» задач, когда есть чёткое ТЗ, расписанная архитектура и т. п., остаётся только кодить, а после сдать работу. TDD же больше подходит для задач, в которых постоянно что-то меняется, вчера было одно, сегодня другое, на момент начала работы толком даже не понятно было, что требуется, архитектура меняется на глазах и т. д. Ну это так, взгляд в большей мере со стороны, TDD я ещё не освоил, чтобы говорить с уверенностью об областях его применения, но вот с проблемой «страшно что-то менять, потому что всё может сломаться незаметно (ошибка компиляции это цветочки) сталкивался и не раз, когда узнал о юнит-тестировании, сначала воодушевился „вот оно, перед тем как что-то менять пишем тесты, и смело меняем“, но быстро разочаровался, когда понял, что код не рассчитанный изначально на тестирование покрыть тестами весьма и весьма сложно без его изменения, а делать изменения возможности нет.
Некоторый товарищи тут писали что им не дают писать тесты, я думаю это решение программиста, если заложешь в оценку времени время на тесты, то и напишешь. Я знаю двух очень разных программистов, один пишет (писал точно, сейчас не знаю) код очень быстро и никаких тестов не писал, у него было красное лицо и он всегда был в напряжении от срочных задач и багов, когда он ушёл с его кодом было много дурацких проблем (конечно же от ответит что злое начальство не давало писать ему тесты и даже просто написать не сильно уродливый код, подгоняли прям метлой), а второй пишет спокойно и вдумчиво, как минимум делает интеграционные тесты которыми проект мониторится круглые сутки и все спят спокойно. Одна компания, одно руководство, разные программисты. Видимо дело в профессионализме не дающем делать совсем убого.
Хотел бы привести цитату неизвестного автора:
«Если вы сделает быстро, но плохо, то скоро все забудут что вы сделали быстро, но будут помнить что сделали плохо
Если вы сделали хорошо, но делали долго, то скоро все забудут что вы делали долго, но будут помнить что сделали хорошо.
»
А про выход первыми на рынок:
1. С чего вы решили что тесты замедлят этот выход?
2. На какой рынок вы собрались выходить первыми? У все задача первыми выйти на рынок? А первые всегда успешны? Гугл, например, был первым?
Хотел бы привести цитату неизвестного автора:
«Если вы сделает быстро, но плохо, то скоро все забудут что вы сделали быстро, но будут помнить что сделали плохо
Если вы сделали хорошо, но делали долго, то скоро все забудут что вы делали долго, но будут помнить что сделали хорошо.
»
А про выход первыми на рынок:
1. С чего вы решили что тесты замедлят этот выход?
2. На какой рынок вы собрались выходить первыми? У все задача первыми выйти на рынок? А первые всегда успешны? Гугл, например, был первым?
Извините, но это не «неизвестный автор».
Если ты сделал быстро, но плохо, все скоро забудут, что ты сделал быстро, но зато долго будут помнить, что ты сделал плохо. Если ты что-то сделал медленно, но хорошо, то все скоро забудут, что ты работал медленно, но будут помнить, что сделал хорошо.
/С.П. Королев, 1907-1966 гг., советский конструктор космической техники/
Если ты сделал быстро, но плохо, все скоро забудут, что ты сделал быстро, но зато долго будут помнить, что ты сделал плохо. Если ты что-то сделал медленно, но хорошо, то все скоро забудут, что ты работал медленно, но будут помнить, что сделал хорошо.
/С.П. Королев, 1907-1966 гг., советский конструктор космической техники/
Я не пишу юнит тесты потому что… у нас слишком часто меняются требования. И часто изменения по коду могут быть несущественные, тогда как соответствующий тест приходится полностью переделывать.
Опять же как подсказывает опыт, что чем чаще меняются требования, тем больше нужны unit-тесты, т.к. без них вносить изменения в уже написанный функционал достаточно сложно.
Да. Соласен. Но тут имеет место такая ситуация: есть код без тестов, нужно внести минорные изменения. Время на внесение изменений есть (изменение минорное => времни выделено мало), а времени на написание тестов — нет (тестов раньше не было => чтобы их добавить нужно много времени).
По-поводу минорных правок соглашусь, если они на самом деле минорные и тестов на проекте нет, то и писать их наверное не стоит.
Введение системы Unit-тестирования на уже запущенные проекты — это беда. Сейчас на работе пытаюсь «задвинуть» тему Unit-тестирования на рабоатающем проекте. И вижу пока, только одну схему:
1. На весь новый функционал — тесты писать обязательно
2. На уже написанный код писать тесты, только тогда, когда работаешь с этим кодом.
Введение системы Unit-тестирования на уже запущенные проекты — это беда. Сейчас на работе пытаюсь «задвинуть» тему Unit-тестирования на рабоатающем проекте. И вижу пока, только одну схему:
1. На весь новый функционал — тесты писать обязательно
2. На уже написанный код писать тесты, только тогда, когда работаешь с этим кодом.
Если у вас нормальная архитектура (низкая связанность, компонентность), то в случае покрытия тестами компонентов (не классов по отдельности) существует большая вероятность того, что у вас все получится :) В противном случае мне известен только один способ:
1. Покрытие проекта функциональным тестами для фиксации текущего поведения.
2. Жесткий рефакторинг.
1. Покрытие проекта функциональным тестами для фиксации текущего поведения.
2. Жесткий рефакторинг.
"… потому что у меня нет достаточного опыта."
Да, возможно. И возможно также именно по этой причине я один из нескольких людей на проекте (20+ разработчиков), который вообще поднимает вопрос о написании тестов.
У меня создается такое впечатление, что большинство из пишущих комментарии слабо представляет себе, что такое TDD и зачем нужны модульные тесты. Откройте Кента Бека, там черным по белому написано: основная задач тестов ну никак НЕ тестирование кода, а создание хорошего дизайна. Поэтому комментарии типа «на тесты нужно писать тесты», «тесты не дают гарантии работоспособности» и прочие подобные просто показывают полную некомпетенцию их авторов в обсуждаемом вопросе. Я понимаю, что в википедии этого не написано, а блеснуть знаниями очень хочется, но все же.
Зачастую мой код в принципе невозможно автоматизированно протестировать, а только лишь человеком и только лишь «на глаз».
Хреновый у тебя код.
Во-первых, я с Вами на «ты» вроде как не общался.
Во-вторых, код отнюдь не хреновый.
В-третьих, такова специфика — много железа и критериев, которые формализовать если и можно, то уж автоматизированная проверка соответствия этим критериям будет очень сложной, во много раз сложнее, чем сам проект.
Во-вторых, код отнюдь не хреновый.
В-третьих, такова специфика — много железа и критериев, которые формализовать если и можно, то уж автоматизированная проверка соответствия этим критериям будет очень сложной, во много раз сложнее, чем сам проект.
А что за специфика?
Я же сказал, грубо говоря, embedded. И многие вещи оказывается невозможно протестировать автоматически. Более того, из-за того, что зачастую приходится делать «сверхоптимизации», иногда бывает невозможно даже выделить какие-то вещи в отдельные модули, которые можно было бы компилировать под x86, чтобы потом гонять тесты. Кроме того, при перекомпиляции могут возникнуть ещё беды, хотя код пишется максимально портабельно — в тех рамках, в которых это возможно, естественно. О запуске тестов на конечной системе (как и об отладке в обычном понимании этого слова), естественно, и речи идти не может.
не могли бы Вы привести пример какого-нибудь кода (желательно OpenSource) в котором все хорошо (ну или хотя бы не плохо) в плане встроенного тестирования? Тема интересна, но я так до конца видимо ее понять не могу.
WINE?
Tcl?
Хороший вопрос. Надо написать отдельную статью с анализом юнит-тестов в различных open-source проектах. Навскидку, много юнит-тестов есть в проекте Spring.
Например, для класса CollectionUtils есть юнит-тест CollectionUtilsTests, а для класса Assert есть юнит-тесты AssertTests.
Например, для класса CollectionUtils есть юнит-тест CollectionUtilsTests, а для класса Assert есть юнит-тесты AssertTests.
Sign up to leave a comment.
«Я не пишу юнит-тесты, потому что ...» — отговорки