Comments 38
мне понравилось про Чака. И походу я чак ((
Думаю, на вопрос «Почему вы не пишете тесты?» чаще всего ответ «лень».
«TDD — это клево, все о нем пишут, еще больше говорят, давайте и мы будем. В корне неправильно. Очень близко к неподготовленному старту — поймите как это работает, потом уже начинайте.»
Голден, такскзть, вордс!
Голден, такскзть, вордс!
Тенденция радует, побольше статей про TDD, Rspec etc, хороших и разных!
Я вот смотрю на TDD и радуюсь всяко.
И хочу применять.
Проблема в том, что я джаваскриптер. Мои функции — это, в 99% случаев, обработчики событий. Как их покрыть тестами, я не представляю, т.к. тестовая среда сводится к, зачастую, немалому объему HTML-верстки. И проблемы чаще возникают именно там, при взаимодействии с DOM-деревом.
Как быть?
И хочу применять.
Проблема в том, что я джаваскриптер. Мои функции — это, в 99% случаев, обработчики событий. Как их покрыть тестами, я не представляю, т.к. тестовая среда сводится к, зачастую, немалому объему HTML-верстки. И проблемы чаще возникают именно там, при взаимодействии с DOM-деревом.
Как быть?
Хороший вопрос, присоединяюсь. Кто пробовал для js писать тесты?
вот тут говорят, что пишут тесты для js — habrahabr.ru/blogs/tdd/116456/#comment_3778416 надеюсь, расскажут подробнее. тоже интересно как это выглядит на практике, на каких задачах помогает. лично никогда не сталкивался с необходимостью тестирование javascript.
Мы пишем JS тесты, но мы чётко отделяем данные-логику-представление
Вам некогда будет стрелять себе в ногу — вы будете писать тесты!
Не знаю, возможно, в идеальных условиях тесты и впрямь хороши, но в действительно сложных приложениях с большим числом зависимостей и assert-то всунуть бывает некуда…
Я о том, что порой создать для теста объект, идентичный натуральному, практически невозможно. А если и возможно, то не факт что с другим объектом всё будет так же.
Не знаю, возможно, в идеальных условиях тесты и впрямь хороши, но в действительно сложных приложениях с большим числом зависимостей и assert-то всунуть бывает некуда…
Я о том, что порой создать для теста объект, идентичный натуральному, практически невозможно. А если и возможно, то не факт что с другим объектом всё будет так же.
Тесты это здорово, но не всегда удается их нависать.
В книгах по ТДД зачастую описываются сферический код в ваукуме типа приема заказов или справочника сотрудников. Не спорю писать тесты под это легко и просто но к суровой реальности это относится не часто. Особенно если для выполнения тестов нужно воссоздать сложное окружение из кучи сторонних компонентов. Тут я просветления не достиг и тестирую только части, не нуждающиеся в сложном окружении. Это кстати влияет и на сам код — приходится некоторые методы разделять чтобы написать тест под них.
И, имхо, TDD (именно тдд а не юнит тесты) не всегда возможно. Есть класс задач где тесты не могут быть инициатором разработки и дизайна. Все равно в начале нужно продумать архитектуру а потом наращивать мясо.
Вопросы к ТДД гурам:
1. Что вы делаете с тестами если поменялась архитектура? Удаляете, переписываете? Вариант ответа: «ТДД дает сразу правильную архитектуру» не канает.
2. Как тестировать сложное окружение? Тестировать только элементарные блоки? или писать кучу моков?
В книгах по ТДД зачастую описываются сферический код в ваукуме типа приема заказов или справочника сотрудников. Не спорю писать тесты под это легко и просто но к суровой реальности это относится не часто. Особенно если для выполнения тестов нужно воссоздать сложное окружение из кучи сторонних компонентов. Тут я просветления не достиг и тестирую только части, не нуждающиеся в сложном окружении. Это кстати влияет и на сам код — приходится некоторые методы разделять чтобы написать тест под них.
И, имхо, TDD (именно тдд а не юнит тесты) не всегда возможно. Есть класс задач где тесты не могут быть инициатором разработки и дизайна. Все равно в начале нужно продумать архитектуру а потом наращивать мясо.
Вопросы к ТДД гурам:
1. Что вы делаете с тестами если поменялась архитектура? Удаляете, переписываете? Вариант ответа: «ТДД дает сразу правильную архитектуру» не канает.
2. Как тестировать сложное окружение? Тестировать только элементарные блоки? или писать кучу моков?
1. Архитектура постепенно меняется также со стороны тестов сперва, затем переходит в код. (конечно, получается иногда много тестов сломать изменением одного)
2. Насколько сложное? Используем DI. У блоков редко больше 5 зависимостей. Да, приходится писать stub или mock для каждого. Все равно тесты блока выделяются в отдельную группу для которой можно сделать общую инициализацию инжектируемых зависимостей и подправить в каждом конкретном тесте, если надо.
2. Насколько сложное? Используем DI. У блоков редко больше 5 зависимостей. Да, приходится писать stub или mock для каждого. Все равно тесты блока выделяются в отдельную группу для которой можно сделать общую инициализацию инжектируемых зависимостей и подправить в каждом конкретном тесте, если надо.
Не считаю себя «TDD гурой» :), но попробую ответить.
Во-первых, в этой статье опять допустили классическую ошибку по этой теме: смешали TDD и умение писать юнит-тесты. Это 2 абсолютно разных вещи: TDD — это техника записи тестов перед кодом, юнит-тесты — это юнит-тесты :) Поэтому прежде чем практиковать TDD, научитесь писать юнит-тесты и не пытайтесь делать это одновременно — потратите много усилий и вероятно будете разочарованы. Умение писать юнит-тесты — такой же навык, например, как работа с регулярными выражениями. Вначале тяжело, а потом разбираешься и привыкаешь.
По юнит-тестам: уже рекомендованный выше Ошроув (маст-рид) и более академический Мессарош (xunit test patterns).
По ТДД: обе книги Кента Бека «TDD в примерах» и XP, они обе в сети есть.
Еще по архитектуре рекомендую «Чистый код» Р. Мартина («дядя Боб»)
Во-вторых, написание тестов для legacy-приложений, т.е. тех где в архитектуру не была заложена тестируемость (и у которых обычно отсутствуют тесты) — одна из самых сложных задач, под силу только test-гуру. Об этом почему-то практически все забывают. Если вы только начинаете с тестами — не беритесь за это в одиночку, лучше пригласите наемного специалиста и выделите на это бюджет (деньги/время).
В-третьих, обязательно нужно разделять виды тестов: юнит-тесты (про которые везде пишут), интеграционные тесты и системные тесты (про вторые и третьи намного меньше).
Юнит — все знают, тестируется один класс без внешних зависимостей (файловая система, бд, сеть — это заменяется заглушками). Работают везде, очень быстры, покрывают максимум кода.
Интеграционные тесты — тестируется взаимодействие нескольких классов вместе, по характеристикам обратны юнит-тестам: обычно внешние системы не подменяются, нужна настройка окружения, выполняются намного дольше, покрывают только нужные куски кода.
Системные тесты — тестируют систему сверху донизу, являются выполняемым ТЗ заказчика. Похожие на интеграционные тесты, но затрагивают всю систему, поэтому обычно выполняются еще дольше, сюда также относится тесты UI, внешних сервисов и т.п.
Очень важно разделять тесты в разные папки по типам, иначе они перемешаются и сложности настройки и эксплуатации интегр. и системных тестов убьют желание у разработчиков запускать вообще что бы то ни было.
1. Тесты зависят не от архитектуры, а от требований заказчика. Если — архитектура поменялась, а требования нет — тесты нужно подстроить под новую архитектуру. В плане ТДД — тесты меняются до изменения кода, поэтому «изменение архитектуры» происходит одновременно с изменениями тестов. Смысл ТДД — тесты двигаются чуть впереди кода и все действие идет маленькими шажками.
2. Тестирование элементарных блоков — это юнит, все зависимости подменяются моками. На все их писать необязательно, если метод очень простой (например, обычный геттер/сеттер), то только время убьете. Или у вас есть функция которая выполняет запрос к базе, типа такой:
смысла писать на нее юнит-тест нет, а вот интеграционный — стоит.
Написание тестов до основного кода очень сильно влияет на конечную архитектуру приложения, она сама начинает делиться на слои, что в итоге дает более удобную и гибкую архитектуру.
Основное правило: если вам тяжело писать тест — меняйте архитектуру.
Пишите тесты и все у вас получится :)
Во-первых, в этой статье опять допустили классическую ошибку по этой теме: смешали TDD и умение писать юнит-тесты. Это 2 абсолютно разных вещи: TDD — это техника записи тестов перед кодом, юнит-тесты — это юнит-тесты :) Поэтому прежде чем практиковать TDD, научитесь писать юнит-тесты и не пытайтесь делать это одновременно — потратите много усилий и вероятно будете разочарованы. Умение писать юнит-тесты — такой же навык, например, как работа с регулярными выражениями. Вначале тяжело, а потом разбираешься и привыкаешь.
По юнит-тестам: уже рекомендованный выше Ошроув (маст-рид) и более академический Мессарош (xunit test patterns).
По ТДД: обе книги Кента Бека «TDD в примерах» и XP, они обе в сети есть.
Еще по архитектуре рекомендую «Чистый код» Р. Мартина («дядя Боб»)
Во-вторых, написание тестов для legacy-приложений, т.е. тех где в архитектуру не была заложена тестируемость (и у которых обычно отсутствуют тесты) — одна из самых сложных задач, под силу только test-гуру. Об этом почему-то практически все забывают. Если вы только начинаете с тестами — не беритесь за это в одиночку, лучше пригласите наемного специалиста и выделите на это бюджет (деньги/время).
В-третьих, обязательно нужно разделять виды тестов: юнит-тесты (про которые везде пишут), интеграционные тесты и системные тесты (про вторые и третьи намного меньше).
Юнит — все знают, тестируется один класс без внешних зависимостей (файловая система, бд, сеть — это заменяется заглушками). Работают везде, очень быстры, покрывают максимум кода.
Интеграционные тесты — тестируется взаимодействие нескольких классов вместе, по характеристикам обратны юнит-тестам: обычно внешние системы не подменяются, нужна настройка окружения, выполняются намного дольше, покрывают только нужные куски кода.
Системные тесты — тестируют систему сверху донизу, являются выполняемым ТЗ заказчика. Похожие на интеграционные тесты, но затрагивают всю систему, поэтому обычно выполняются еще дольше, сюда также относится тесты UI, внешних сервисов и т.п.
Очень важно разделять тесты в разные папки по типам, иначе они перемешаются и сложности настройки и эксплуатации интегр. и системных тестов убьют желание у разработчиков запускать вообще что бы то ни было.
1. Тесты зависят не от архитектуры, а от требований заказчика. Если — архитектура поменялась, а требования нет — тесты нужно подстроить под новую архитектуру. В плане ТДД — тесты меняются до изменения кода, поэтому «изменение архитектуры» происходит одновременно с изменениями тестов. Смысл ТДД — тесты двигаются чуть впереди кода и все действие идет маленькими шажками.
2. Тестирование элементарных блоков — это юнит, все зависимости подменяются моками. На все их писать необязательно, если метод очень простой (например, обычный геттер/сеттер), то только время убьете. Или у вас есть функция которая выполняет запрос к базе, типа такой:
function getClients($id) { return $this->db->query("SELECT * FROM clients WHERE id = $id"); }
смысла писать на нее юнит-тест нет, а вот интеграционный — стоит.
Написание тестов до основного кода очень сильно влияет на конечную архитектуру приложения, она сама начинает делиться на слои, что в итоге дает более удобную и гибкую архитектуру.
Основное правило: если вам тяжело писать тест — меняйте архитектуру.
Пишите тесты и все у вас получится :)
спасибо за такое подробное дополнение! но unit-тесты и TDD я как раз разделяю в статье, так что «классической ошибки» нет;)
это вообще два разных навыка — умение писать тесты и TDD. в статье на этом несколько раз акцентируется внимание.
в докладе я более подробно на этом останавливался — статью решил не раздувать.
это вообще два разных навыка — умение писать тесты и TDD. в статье на этом несколько раз акцентируется внимание.
в докладе я более подробно на этом останавливался — статью решил не раздувать.
Ну ясного разделения я не увидел, а главное предупреждения что не нужно пытаться начинать с обеими вещами одновременно. Но это мое мнение, за статью спасибо.
предупреждение тут: Риск остаться без ноги -> Неподготовленный старт
согласен с вами полностью в вопросе разделения. рекомендую всем начинать именно с написания тестов после функциональности, а уже потом, освоив первый навык, переходить к TDD.
спасибо за спасибо:)
согласен с вами полностью в вопросе разделения. рекомендую всем начинать именно с написания тестов после функциональности, а уже потом, освоив первый навык, переходить к TDD.
спасибо за спасибо:)
>рекомендую всем начинать именно с написания тестов после функциональности, а уже потом, освоив первый навык, переходить к TDD
Довольно большой риск в таком случае не перейти к TDD никогда, по-моему. Потому что писать тесты на код, который не был создан с учётом возможности тестирования зачастую большая проблема. Вплоть до того, что юнитом будет выступать вся программа, а «юнит»-тестом нужно будет создавать окружение (включая БД), эмулировать действия пользователя и перехватывать вывод программы. Надо ли говорить, что написать такой тест даже для одной ветви выполнения совсем не просто, не говоря о том, чтобы покрыть весь код. И если решение использовать тесты и TDD принято только на энтузиазме, то он быстро может кончиться.
Довольно большой риск в таком случае не перейти к TDD никогда, по-моему. Потому что писать тесты на код, который не был создан с учётом возможности тестирования зачастую большая проблема. Вплоть до того, что юнитом будет выступать вся программа, а «юнит»-тестом нужно будет создавать окружение (включая БД), эмулировать действия пользователя и перехватывать вывод программы. Надо ли говорить, что написать такой тест даже для одной ветви выполнения совсем не просто, не говоря о том, чтобы покрыть весь код. И если решение использовать тесты и TDD принято только на энтузиазме, то он быстро может кончиться.
Не гуру, но своё ИМХО вставить попробую:
1) Архитектуру нужно основательно продумывать заранее, да и как часто её действительно необходимо менять?
2) Например, habrahabr.ru/blogs/java/72617/, мок-библиотеки — нет необходимости самостоятельно описывать моки. Помимо этого существуют общие рекомендации по сложности кода, даже выбрать есть из чего. Например, описаны такие общеизвестные метрики как сцепление(CBO) и связность, глубина дерева наследования (DIT)… TDD даёт возможность превышать рекомендованные значения метрик Weightened Methods per Class, Response For Class / Number Of Methods (ajusted RFC) и прочих, без боязни того, что изменения вызовут неожиданный эффект ряби и необходимость в авральном порядке искать ошибки.
1) Архитектуру нужно основательно продумывать заранее, да и как часто её действительно необходимо менять?
2) Например, habrahabr.ru/blogs/java/72617/, мок-библиотеки — нет необходимости самостоятельно описывать моки. Помимо этого существуют общие рекомендации по сложности кода, даже выбрать есть из чего. Например, описаны такие общеизвестные метрики как сцепление(CBO) и связность, глубина дерева наследования (DIT)… TDD даёт возможность превышать рекомендованные значения метрик Weightened Methods per Class, Response For Class / Number Of Methods (ajusted RFC) и прочих, без боязни того, что изменения вызовут неожиданный эффект ряби и необходимость в авральном порядке искать ошибки.
да, наставник — это здорово. лучший вариант — наставник-коллега. так как на тренингах вам скорее всего перескажут книжки и покажут пару простых примеров. книги, статьи, youtube — и уже есть какая-то база, на которую можно опираться. все зависит от желания.
Самостоятельное обучение всему (почти) чему угодно без «фидбэка» всегда (почти) сложнее, чем под чутким руководством опытного наставника (особенно если это действительно опытный наставник, а не «всего лишь» гуру в своей области).
Статья хорошая, понравилась. Еще бы сравнение с альтернативными вариантами разработки классов и методов не помешало. Кстати, какие вообще реально альтернативы бывают?
наглядное сравнение работы программиста с использованием unit-тестов и без них было в докладе (отобразить это в статье достаточно трудно, так что не стал и пытаться) — добавлю ссылку как только будет готово видео.
отдельно какие-то альтернативы я бы выделять не стал. есть набор хороших практик, которые помогают работать более эффективно. применять их или нет — зависит от желания и возможностей разработчика. для многих практик придуманы очень красивые аббревиатуры и абстракции:) хотя на самом деле это просто разумные подходы к решению задач, которые применяются людьми уже много лет. и не только в программировании:)
отдельно какие-то альтернативы я бы выделять не стал. есть набор хороших практик, которые помогают работать более эффективно. применять их или нет — зависит от желания и возможностей разработчика. для многих практик придуманы очень красивые аббревиатуры и абстракции:) хотя на самом деле это просто разумные подходы к решению задач, которые применяются людьми уже много лет. и не только в программировании:)
Есть ли автоматические способы тестирования GUI?
да. для web, например, есть Selenium.
Unit-тесты предназначены для тестирования логики и поведения отдельных классов.
Если обработчик нажатия кнопки на пользовательском интерфейсе содержит какую-то бизнес-логику, то это сигнал пересмотреть архитектуру приложения и, как минимум, разделить, слои (layers).
Если обработчик нажатия кнопки на пользовательском интерфейсе содержит какую-то бизнес-логику, то это сигнал пересмотреть архитектуру приложения и, как минимум, разделить, слои (layers).
Это не отменяет необходимости тестировать класс-обертку для кнопки, в том числе, что вызывается обработчик при нажатии.
Практически отменяет. Если кнопка «обновить» вызывает метод metamodel.Refresh(true) то тестировать надо метод модели, а не кнопку — кнопку (то что если на нее нажать — возникнет событие) уже протестировал разработчик используемого вами библиотеки пользовательского интерфейса.
Если такая библиотека используется. Но даже если используется, это не значит, что кнопку вообще тестировать не надо. Например то, что она существует в принципе, что она видима пользователю и активна, что у неё надпись правильная и т. п.
Еще раз. То что при изменении свойства Visible кнопки меняется ее видимость — это тестировали за вас. Правильную в данном конкретном случае надпись кнопки тоже устанавливает модель и именно там и надо писать модульный тест, подставляя вместо кнопки какой-нибудь mock и проверяя в конце теста что надпись действительно изменилась.
Модульные тесты — это максимально изолированные тесты без внешних зависимостей, таких как файловая система, база данных, библиотека пользовательского интерфейса, парсер html который из <button> нарисует на экране кнопку и т.д.
Модульные тесты — это максимально изолированные тесты без внешних зависимостей, таких как файловая система, база данных, библиотека пользовательского интерфейса, парсер html который из <button> нарисует на экране кнопку и т.д.
Под видимостью я имел в виду, что свойство Visible установлено, что в том же html вообще тэг button есть (парсером будет сам тест), что в этом тэге есть атрибут onclick с нужным значением и т. п. В общем под тестирование UI я имею в виду прежде всего тестирование слоя V в терминах MVC, ну и C к UI тоже относится.
Чак Норрис никогда не спит. Он ждёт. :)
Хорошая статья, спасибо.
Imho, было бы неплохо также сравнить TDD с BDD, которое является развитием первого.
Хорошая статья, спасибо.
Imho, было бы неплохо также сравнить TDD с BDD, которое является развитием первого.
Sign up to leave a comment.
Как не выстрелить себе в ногу