Comments 70
UFO just landed and posted this here
Это всего лишь одна из граней. После 1го проекта с тестами — чувствуешь себя без них голым.
Плюсов огромное количество и те, что в выводе просто самые очевидные, но к ним можно ещё дописать много бонусов. Можно собраться спроектировать, нарисовать, задокументировать и написать. А потом внести изменение и надеяться, что не упадёт (или проверять весь функционал). А можно писать начиная с тестов (это фактически вместо проектирования) и потом после изменений если упадёт — то ты узнаешь об этом до сдачи заказчику.
Истерии никакой нет, просто когда человек открывает для себя какой-то удобный инструмент — он пытается поделиться им и знанием с коллегами. Видимо инструмент действительно достойный, раз это выглядит как истерия…
Плюсов огромное количество и те, что в выводе просто самые очевидные, но к ним можно ещё дописать много бонусов. Можно собраться спроектировать, нарисовать, задокументировать и написать. А потом внести изменение и надеяться, что не упадёт (или проверять весь функционал). А можно писать начиная с тестов (это фактически вместо проектирования) и потом после изменений если упадёт — то ты узнаешь об этом до сдачи заказчику.
Истерии никакой нет, просто когда человек открывает для себя какой-то удобный инструмент — он пытается поделиться им и знанием с коллегами. Видимо инструмент действительно достойный, раз это выглядит как истерия…
UFO just landed and posted this here
Никто и не говорит о панацее. Я лично для себя их использую чтоб быть уверенным, что после рефакторинга всё будет работать. Мне не нужно будет что-то проверять, я просто запущу тесты и узнаю работает или нет. Я пишу тест один раз и потом всегда (весь жизненный цикл программы/модуля/класса) уверен в этом участке кода. В итоге это выливается в экономию того самого времени поиска неработающих частей, но да, это всё будет когда потом — сейчас вы действительно тратите время на очевидные и ненужные в данный момент вещи…
При разработке в Agile конец спринта предполагает наличие готового, рабочего, самостоятельного кода/модуля. Т.е. вот если сейчас работает, все — дальше его не трогают, не рефакторят. Все риски минимизируются качественным планированием, ревью кода, гейтед чекинами.
Вопрос: зачем тратить время на написание тестов для кода, который работает?
Лично я, глубоко внутри, осознаю потенциальную полезность юнит-тестов, но моя практическая часть отказывается тратить на них время. Тесты пишу, но пользы от них не вижу. Как-то так.
Вопрос: зачем тратить время на написание тестов для кода, который работает?
Лично я, глубоко внутри, осознаю потенциальную полезность юнит-тестов, но моя практическая часть отказывается тратить на них время. Тесты пишу, но пользы от них не вижу. Как-то так.
я смотрю вы и команда идеальные программисты! Пишите без ошибок всегда. А agile та еще фигня, больше трепа, чем действий
Неустойчивость требований влечет необходимость обсуждений и внесения изменений.
Чем выше степень неустойчивости — тем органичнее смотрится Agile. И наоборот.
Чем выше степень неустойчивости — тем органичнее смотрится Agile. И наоборот.
Про Agile все-таки наоборот — минимум трепа, максимум действий.
Ничего не имею против Agile, но как в одной из статей заметили:
«В русской расскладке „Agile“, звучит как „Фпшду“, что как-бы настораживает»(с) из одной из множества хабро статей.
«В русской расскладке „Agile“, звучит как „Фпшду“, что как-бы настораживает»(с) из одной из множества хабро статей.
Как вы распознаете код как рабочий? Держу пари, вы его тестируете. Раз не пишете тесты, значит тестируете руками. Ок, вы провели новую итерацию, вы гарантируете, что код рабочий? Нет, пока не протестируете. Или вы переписали кусок кода (да-да, такое случается, когда проект развивается). Где гарантии, что где-то что-то не отвалилось? Опять нужно тестировать. А теперь вопрос на смекалку: кто быстрее проверит код, вы или RSpec?
Увидел в начале комментария слово «Agile», и подумал, что он будет в поддержку тестирования. Наверное, я совсем не понимаю, что такое Agile. Как можно практиковать Agile без тестирования? Agile ведь подразумевает, что код будут рефакторить?.. Или вы просто упомянули Agile, но сами его не практикуете?
Да, я знаю, что в манифесте Agile нет про тестирование, но не понимаю, как можно строить процесс разработки, основанный на непрерывном рефакторинге, без использования тестов. Сам я в тех моментах, где приходится делать рефакторинг, уже сильно запарился ручками проводить тесты, и всё-равно вылезают баги. На собственном опыте убедился в необходимости тестирования.
Да, я знаю, что в манифесте Agile нет про тестирование, но не понимаю, как можно строить процесс разработки, основанный на непрерывном рефакторинге, без использования тестов. Сам я в тех моментах, где приходится делать рефакторинг, уже сильно запарился ручками проводить тесты, и всё-равно вылезают баги. На собственном опыте убедился в необходимости тестирования.
Спасибо, что обратили внимание на этот момент. Действительно, выглядит так, что я сравниваю самую тупую реализацию с более продуманной и отношу это к преимуществам техники TDD. Безусловно можно было бы немного напрячься и написать сразу то же самое без тестов, но
Я ни в коем случае не пытаюсь сказать что TDD это единственно правильный способ писать программы, тем более что ни одной программы через TDD в чистом виде я не написал. Я пробую инструмент, вижу определенные его преимущества и мне этот опыт показался достаточно интересным, чтобы им поделиться.
- Если я уже продумываю юз-кейсы, почему бы их тут же не записать?
- Лично я бы не стал выдумывать юз-кейсы класса, а начал бы писать класс-пользователь юнита — боевую сцену, выясняя что ей требуется от юнита и добавляя по ходу. Это провоцирует писать сначала процедурный код типа damageTakerUnit.setHealth(damageTakerUnit.getHealth() — damageDealerUnit.getDamage()) вместо damageDealerUnit.dealDamage(damageTakerUnit), а потом уже рефакторить. На реальных проектах я достаточно часто видел как написав работающий процедурный говнокод, на рефакторинг забивают. Последствия в долгосрочной перспективе весьма печальные.
- У этого класса сначала 3 юз-кейса: создать, продамажить, попросить продамажить другой юнит. Их действительно просто продумать и закодить без тестов. Когда нужно будет добавить еще несколько, это уже не будет так просто. Наличие тестов позволит добавлять юз-кейсы инкрементально, не беспокоясь, что предыдущие сломаются
Я ни в коем случае не пытаюсь сказать что TDD это единственно правильный способ писать программы, тем более что ни одной программы через TDD в чистом виде я не написал. Я пробую инструмент, вижу определенные его преимущества и мне этот опыт показался достаточно интересным, чтобы им поделиться.
UFO just landed and posted this here
Это вопрос уже про процедурное vs ООП. Мне ООП однозначно ближе. В описанном случае я бы просто раздавал глобальный модификатор всем юнитам — в конкретном случае моей боевой системы может быть максимум 16 юнитов в бою. В абстрактном случае, когда бы это не подошло (сотни или тысячи юнитов, глобальный модификатор часто меняется) завел бы отдельный объект глобальных модификаторов и при создании юнита давал бы на него ссылку — это как пример, но абсолютно точно я бы не стал выносить расчет урона из юнита. Процедурный подход дает некую гибкость, пока проект маленький, но когда проект вырастет я хочу чтобы всё, что может произойти с юнитом, было описано в классе юнита, а не в пяти разных местах, где он используется.
UFO just landed and posted this here
UFO just landed and posted this here
Мы применяли такой подход в одном из проектов, писали тесты только на веб-сервисы, не трогая более мелкие классы. Опыт вполне успешный — отлавливалась куча ошибок, облегчалась поддержка.
Я сейчас хочу попробовать, хотя бы в качестве эксперимента, другой подход, когда весь код пишется через тесты. Когда (и если: ) доведу проект до какого-то рабочего состояния, обязательно напишу про этот опыт.
Я сейчас хочу попробовать, хотя бы в качестве эксперимента, другой подход, когда весь код пишется через тесты. Когда (и если: ) доведу проект до какого-то рабочего состояния, обязательно напишу про этот опыт.
Соглашусь насчет параноидальности тестирования. Но выскажу пару моментов как я на них смотрю:
1) нужно и думать что тестировать, то есть под приоритет тестирования подпадает внешний интерфейс класса, ну или максимум еще и защищенный, такой который используется из вне, и лишь в редких случаях можно писать тесты на особо сложные внутренние методы, с особенно сложной логикой, где вероятна ошибка. Это позволяет сократить количество тестов, продумать именно внешний API класса, который важен для системы в целом, так как через него другие классы взаимодействуют с тестируемым, и если была допущена ошибка во внутренних методах, используемых во внешнем — это будет заметно по падению тестов на внешние классы;
2) автор конечно привел пример разработки совершенно простого класса по методике TDD. Он писал unit-тесты, но ведь кроме unit-тестирования есть и другие методы тестирования кода, кроме unit. Так что более глобальное взаимодействие можно тестировать написанием к примеру интеграционных тестов.
3) как вариант использовать не более приземленный TDD подход, а более абстрагированный BDD подход, для более высокоуровневых тестов и собственно тестирования приложения с точки зрения пользователя к прмиеру.
1) нужно и думать что тестировать, то есть под приоритет тестирования подпадает внешний интерфейс класса, ну или максимум еще и защищенный, такой который используется из вне, и лишь в редких случаях можно писать тесты на особо сложные внутренние методы, с особенно сложной логикой, где вероятна ошибка. Это позволяет сократить количество тестов, продумать именно внешний API класса, который важен для системы в целом, так как через него другие классы взаимодействуют с тестируемым, и если была допущена ошибка во внутренних методах, используемых во внешнем — это будет заметно по падению тестов на внешние классы;
2) автор конечно привел пример разработки совершенно простого класса по методике TDD. Он писал unit-тесты, но ведь кроме unit-тестирования есть и другие методы тестирования кода, кроме unit. Так что более глобальное взаимодействие можно тестировать написанием к примеру интеграционных тестов.
3) как вариант использовать не более приземленный TDD подход, а более абстрагированный BDD подход, для более высокоуровневых тестов и собственно тестирования приложения с точки зрения пользователя к прмиеру.
Насчет BDD Вы совершенно правы.
Этот подход действительно приносит большую пользу, в отличие от более слабого TDD.
Документирующие тесты, которые можно читать непрограммисту — это стоит того, чтобы применять данный подход.
Вот только это должны быть не тесты реализации, а тесты спецификации, протокола взаимодействия.
Иначе это не BDD, и коэффициент полезности будет недалек от нуля.
Этот подход действительно приносит большую пользу, в отличие от более слабого TDD.
Документирующие тесты, которые можно читать непрограммисту — это стоит того, чтобы применять данный подход.
Вот только это должны быть не тесты реализации, а тесты спецификации, протокола взаимодействия.
Иначе это не BDD, и коэффициент полезности будет недалек от нуля.
Есть огромная куча типов тестирования, в данном случае функциональное тестирование как нельзя лучше подходит. Можно делать более «высокоуровневое » тестирование, но тогда возрастает вероятность пропустить баг. Для такого типа тестирования обычно нанимаются тестировщики и тест дизайнеры.
Когда проект вырастет я хочу чтобы всё, что может произойти с юнитом, было описано в классе юнита, а не в пяти разных местах, где он используется.
IMHO, Вы еще не достигли просветления.
Все, что может произойти с unit'ом, в один класс может оказаться очень трудно упихать.
И это будет напоминать именно упихивание, а не архитектуру.
ООП придуман для распиливания обязанностей на части.
Если обязанностей становится много — необходимо разделить по принципу единой ответственности.
IMHO, Вы еще не достигли просветления.
Я и не претендую =) С каждым новым проектом понимание растет и меняется.
Принцип единой ответственности говорит не только о том, что каждый класс должен отвечать за одну вещь, но и о том, что за одну вещь в программе должен отвечать ровно один класс. Если какой-то аспект работы класса становится слишком сложным, я выделяю этот аспект в отдельный класс и использую его внутри первого класса.
Тут речь идет не о том, чтобы запихать весь код в один класс, но все логические операции с юнитом должны делаться через интерфейс юнита. Мой опыт говорит за то, что такой подход лучше всего помогает понимать и поддерживать код.
Чтобы продумать конечно писать тесты не обязательно. Можно писать комментарии или следовать не TDD, а BDD, но суть одна. Если человек действительно привык и приучился покрывать код тестами перед началом написания кода, то он уже автоматически перед написанием теста задумается, что должен делать его код. Помимо немного потраченного времени на тесты сейчас, можно получить такие профиты как: код покрыт тестами, значит легче рефакторить проект (как бы идеально проект не был написан, проект развивается и так или иначе будет переписан тот или иной кусок кода, причем не факт, что изначальным автором кода), мы получаем более продуманный код, и конечно же мы получаем своеобразную документацию, которая описывает, что этот код сделает.
Так что, я сказал бы, что да, можно просто хорошо продумывать код до его написания и без тестов, просто при TDD/BDD мы получаем еще некоторые плюшки и самодисциплина написать тест будет лишним мотиватором продумать код.
Так что, я сказал бы, что да, можно просто хорошо продумывать код до его написания и без тестов, просто при TDD/BDD мы получаем еще некоторые плюшки и самодисциплина написать тест будет лишним мотиватором продумать код.
я пожалуй выскажусь насчет темы, а то вдруг кто-то и правду решит использовать как есть.
самое больное:
0) BDD/TDD и прочее — ориентируйтесь на голову, а не тактику. я понимаю, что пример простой. но прежде всего — голова должна быть. BDD должно помогать, а не тормозить и связывать руки. так что ищите аргументы за/против в каждом локальном случае.
1) не аргумент в пользу. менеджер будет паниковать независимо от выбранного метода работы
2) опять ООП. после знакомства с mockito обычно приходит понимание что ООП не то чтобы не критично, а просто неважно. а в реальной жизни (т.е. проектах) ООП используется чаще для абстракции (я имею ввиду миксины). не беру в расчет разработку API — это не тот уровень.
3) покрытие кода проверяется. это не сложно. и если вы напишете сеттер не правильно — то упасть должен не тест «на сеттер», а половина остальных тестов. плюс один простой тест погоды не сделает.
4) живая документация. как много в жтом слове. я очень рад что jdk писалось с использованием javadocs. Вы правда верите что человек полезет смотреть «все» тесты для этого класса или метода, особенно если каждый тест должен быть простым и проверять отдельный аспект? документация должна быть, если она требуется.
5) требования рулят. добавить нечего.
6)… и это никак не поможет объяснить менеджеру, почему опять задержка. сарказм, что ли. это никак не относится к BDD, потому что — в любом подходе у тебя есть код и тесты, неважно что из них написано раньше по времени
поясню — смотрю на это не с точки зрения сферического коня в вакууме, а в контексте реальных сделанных и текущих проектов. в конце концов — все там будем.
а вообще — развлекайтесь, что уж.
самое больное:
0) BDD/TDD и прочее — ориентируйтесь на голову, а не тактику. я понимаю, что пример простой. но прежде всего — голова должна быть. BDD должно помогать, а не тормозить и связывать руки. так что ищите аргументы за/против в каждом локальном случае.
1) не аргумент в пользу. менеджер будет паниковать независимо от выбранного метода работы
2) опять ООП. после знакомства с mockito обычно приходит понимание что ООП не то чтобы не критично, а просто неважно. а в реальной жизни (т.е. проектах) ООП используется чаще для абстракции (я имею ввиду миксины). не беру в расчет разработку API — это не тот уровень.
3) покрытие кода проверяется. это не сложно. и если вы напишете сеттер не правильно — то упасть должен не тест «на сеттер», а половина остальных тестов. плюс один простой тест погоды не сделает.
4) живая документация. как много в жтом слове. я очень рад что jdk писалось с использованием javadocs. Вы правда верите что человек полезет смотреть «все» тесты для этого класса или метода, особенно если каждый тест должен быть простым и проверять отдельный аспект? документация должна быть, если она требуется.
5) требования рулят. добавить нечего.
6)… и это никак не поможет объяснить менеджеру, почему опять задержка. сарказм, что ли. это никак не относится к BDD, потому что — в любом подходе у тебя есть код и тесты, неважно что из них написано раньше по времени
поясню — смотрю на это не с точки зрения сферического коня в вакууме, а в контексте реальных сделанных и текущих проектов. в конце концов — все там будем.
а вообще — развлекайтесь, что уж.
Разве это BDD? Я думал, BDD — это описание действий пользователя, причем оперируя сущностями, доступными пользователю. А в топике — TDD.
На мой взгляд в тестах как раз описания возможных действий с классом пользовательским кодом. Можете написать пару тестовых методов на класс Unit для примера, как они выглядели бы в вашем понимании BDD?
Ради красоты картинки нарисовали какой-то чудовищный процесс, когда в одном цикле постоянно пишем падающие тесты, а во втором параллельно пытаемся их починить %)
Но вы правы (почитал внимательно другие источники), BDD работает на более высоком уровне. Я был свято уверен, что BDD — тот же TDD, но с другой нотацией названий тестов и их написания (given-when-then). Вы открыли мне глаза, спасибо! Уберу упоминание BDD из топика.
Но вы правы (почитал внимательно другие источники), BDD работает на более высоком уровне. Я был свято уверен, что BDD — тот же TDD, но с другой нотацией названий тестов и их написания (given-when-then). Вы открыли мне глаза, спасибо! Уберу упоминание BDD из топика.
Не обязательно. Тут где-то была статься от Саши Косса (а, вот она — habrahabr.ru/company/evilmartians/blog/149335/ ), где он довольно наглядно указал на отличия TDD от BDD.
При юнит-тестировании чаще всего мы имеем дело с объектами и функциями, с которыми пользователь напрямую не соприкасается. Но описывая в тестах их _поведение_, мы используем именно BDD-подход.
При юнит-тестировании чаще всего мы имеем дело с объектами и функциями, с которыми пользователь напрямую не соприкасается. Но описывая в тестах их _поведение_, мы используем именно BDD-подход.
Ответил предыдущему оратору. Дополняю.
В свою поддержку цитирую приведенную вами статью:
В BDD мы смотрим на это под другим углом:
Когда к статье пользователя кто-либо постит комментарий, мы должны послать ему e-mail, если он включил нотификации о новых комментариях.
Для TDD и BDD используются разные инструменты. К примеру, книга The RSpec Book описывает использование TestUnit (MiniTest) или RSpec для TDD и Cucumber для BDD — в едином процессе, как на майрософтовской даграммке.
В свою поддержку цитирую приведенную вами статью:
В BDD мы смотрим на это под другим углом:
Когда к статье пользователя кто-либо постит комментарий, мы должны послать ему e-mail, если он включил нотификации о новых комментариях.
Для TDD и BDD используются разные инструменты. К примеру, книга The RSpec Book описывает использование TestUnit (MiniTest) или RSpec для TDD и Cucumber для BDD — в едином процессе, как на майрософтовской даграммке.
Найти в статье слово «пользователь» и привести цитату с ним — не аргумент. В той же статье описывается BDD-подход к тестированию класса, где действиями пользователя и не пахнет.
Если бы BDD-практика оперировала лишь действиями пользователя, ее бы назвали Use Flow Driven Development, или как-то так. BDD — это не «когда Cucumber», для BDD можно с тем же успехом использовать RSpec. Коль скоро мы переходим от test(«call function with n arguments») к it(«should do blablabla»), мы переходим от TDD к BDD.
Другое дело, что тот же Cucumber для TDD не приспособлен в принципе, но это уже другой разговор.
Если бы BDD-практика оперировала лишь действиями пользователя, ее бы назвали Use Flow Driven Development, или как-то так. BDD — это не «когда Cucumber», для BDD можно с тем же успехом использовать RSpec. Коль скоро мы переходим от test(«call function with n arguments») к it(«should do blablabla»), мы переходим от TDD к BDD.
Другое дело, что тот же Cucumber для TDD не приспособлен в принципе, но это уже другой разговор.
Замена синтаксиса test(«call function with n arguments») к it(«should do blablabla») вообще ничего не значит. Те же яйца, только в профиль. Что же касается смены подхода, описанной в сабже, — это уже значимо, но тем не менее это лишь более продуманный подход к TDD.
BDD — это нечто большее. И Cucumber для TDD именно потому и не приспособлен, что решает принципиально иную задачу.
Если не верите статье Microsoft, вот картинка «Цикл BDD» из The RSpec Book: Behaviour-Driven Development with RSpec, Cucumber, and Friends:
(кликабле)
Почитайте ну хотя бы на Википедии, как в BDD описываются тест-кейсы. Юнит-тестированием там и не пахнет.
en.wikipedia.org/wiki/Behavior-driven_development#Behavioral_specifications
Если надо, могу перевести выдержку из этого параграфа на русский.
BDD — это нечто большее. И Cucumber для TDD именно потому и не приспособлен, что решает принципиально иную задачу.
Если не верите статье Microsoft, вот картинка «Цикл BDD» из The RSpec Book: Behaviour-Driven Development with RSpec, Cucumber, and Friends:
(кликабле)
Почитайте ну хотя бы на Википедии, как в BDD описываются тест-кейсы. Юнит-тестированием там и не пахнет.
en.wikipedia.org/wiki/Behavior-driven_development#Behavioral_specifications
Если надо, могу перевести выдержку из этого параграфа на русский.
Картинка показывает использование RSpec для.юнит-тестирования и Cucumber для интеграционных тестов. Почти в самом начале The RSpec Book дано определение BDD, и оно никоим образом не относится к «user story».
В приведенной же статье из вики приведен пример BDD-спеки для RSpec: en.wikipedia.org/wiki/Behavior-driven_development#Story_versus_Specification
В приведенной же статье из вики приведен пример BDD-спеки для RSpec: en.wikipedia.org/wiki/Behavior-driven_development#Story_versus_Specification
> Почти в самом начале The RSpec Book дано определение BDD, и оно никоим образом не относится к «user story».
Эта книга сначала рассказывает о юнит-тестировании и только во второй главе начинается речь о BDD. После подробной постановки проблемы, на 121-й странице дается «A Description of BDD» (описание BDD), и звучит оно так:
Behaviour-Driven Development is about implementing an application by
describing its behavior from the perspective of its stakeholders.
BDD — это разработка приложения через описание его поведения с точки зрения заинтересованных лиц.
Эта книга сначала рассказывает о юнит-тестировании и только во второй главе начинается речь о BDD. После подробной постановки проблемы, на 121-й странице дается «A Description of BDD» (описание BDD), и звучит оно так:
Behaviour-Driven Development is about implementing an application by
describing its behavior from the perspective of its stakeholders.
BDD — это разработка приложения через описание его поведения с точки зрения заинтересованных лиц.
Стоит сказать, что ни в коем случае не отвергаю использование «user stories» для интеграционных тестов (там они более чем удобны). Но я категорически не согласен с мнением, что BDD — это только интеграционные тесты.
Ну ок, а что тогда BDD такое? Это интеграционные тесты с использованием Capybara (к примеру), и все остальные, где используется такой синтаксис?
Тогда у нас какая-то нечеткая разница получается.
it "should..." do
# ...
end
Тогда у нас какая-то нечеткая разница получается.
А разница тут действительно довольно нечеткая, отсюда и столько споров. Из той же вики:
> At its core, Behavior-driven development is a specialized version of test-driven development which focuses on behavioral specification of software units.
Основной причиной создания BDD послужило то, что глядя на TDD-спеки не всегда понятно, что должен выполнять тот или иной модуль. Глядя же на BDD-спеки, мы видим практически документацию, руководство к действию.
> At its core, Behavior-driven development is a specialized version of test-driven development which focuses on behavioral specification of software units.
Основной причиной создания BDD послужило то, что глядя на TDD-спеки не всегда понятно, что должен выполнять тот или иной модуль. Глядя же на BDD-спеки, мы видим практически документацию, руководство к действию.
Основной причиной создания BDD послужило то, что глядя на TDD-спеки не всегда понятно, что должен выполнять тот или иной модуль.
Не понятно для кого, для клиента? Хм… В этом случае, ему будут понятны действительно только интеграционные тесты, если не использовать «второй круг» с Cucumber.
fill_in 'e-mail', with: 'test@test.com'
fill_in 'password', with: 'password'
click_link 'Log In'
page.should have_content 'Logged in!'
Это должно быть понятно абсолютно всем, и можно показывать заказчику. А вот тесты контроллеров, моделей, напичканные проверками на внутренние методы, использующие моки, стабы — там заказчик точно не разберется. Получается, BDD — понятно написанные интеграционные тесты + Cucumber, если он есть.
Да даже самому программисту. Много вот Вы поймете из теста, где описаны результаты без объяснений, откуда они взялись (собственно, описания _поведения_)?
Я нигде не говорил, что «BDD — это только интеграционные тесты»! При чем тут вообще интеграционные тесты? Cucumber к ним никакого отношения не имеет.
Я пытался объяснить, что BDD — это не просто иной, более продуманный способ делать TDD. BDD — это методика разработки, включающая TDD, а также еще один слой тестов, оперирующий концепциями, доступными пользователю.
Первый абзац статьи о BDD в Википедии:
Behavior-driven development … provides software developers and business analysts with shared tools and a shared process to collaborate on software development.
BDD позволяет бизнес-аналитикам принимать участие в разработке вместе с программистами, предлагая им для этого общие инструменты и единый процесс.
Это главное в BDD и является его сутью.
BDD включает в себя TDD в качестве одного из компонентов процесса. Конечно, стиль юнит-тестов в TDD при этом меняется и становится направленным на поведение (сабж как раз расказывает о таком изменении стиля TDD). Именно об этом и говорится в той части статьи, на которую вы ссылаетесь как на «пример BDD-спеки для RSpec»:
…specification-based testing is seen in BDD practice as a complement to story-based testing and operates at a lower level. Specification testing is often seen as a replacement for free-format unit testing.
В методике BDD, тестирование при помощи спецификаций дополняет тестирование при помощи историй и работает на более низком уровне. Тестирование при помощи спецификаций часто рассматривается как замена юнит-тестированию в свободной форме.
Я пытался объяснить, что BDD — это не просто иной, более продуманный способ делать TDD. BDD — это методика разработки, включающая TDD, а также еще один слой тестов, оперирующий концепциями, доступными пользователю.
Первый абзац статьи о BDD в Википедии:
Behavior-driven development … provides software developers and business analysts with shared tools and a shared process to collaborate on software development.
BDD позволяет бизнес-аналитикам принимать участие в разработке вместе с программистами, предлагая им для этого общие инструменты и единый процесс.
Это главное в BDD и является его сутью.
BDD включает в себя TDD в качестве одного из компонентов процесса. Конечно, стиль юнит-тестов в TDD при этом меняется и становится направленным на поведение (сабж как раз расказывает о таком изменении стиля TDD). Именно об этом и говорится в той части статьи, на которую вы ссылаетесь как на «пример BDD-спеки для RSpec»:
…specification-based testing is seen in BDD practice as a complement to story-based testing and operates at a lower level. Specification testing is often seen as a replacement for free-format unit testing.
В методике BDD, тестирование при помощи спецификаций дополняет тестирование при помощи историй и работает на более низком уровне. Тестирование при помощи спецификаций часто рассматривается как замена юнит-тестированию в свободной форме.
От того, что юнит-тесты пишутся не в виде user stories, они не перестали быть BDD. Как я написал в комменте выше, BDD — это развитие TDD, а не дополнение.
То, что вы называете BDD, в терминах статьи Википедии назвается разработкой через тестирование при помощи спецификаций. Тестирование при помощи спецификаций, несомненно, является важным развитием юнит-тестирования.
Но TDD от этого не превращается в BDD, как не превратится в конвейер автомеханик от того, что упорядочит процесс сборки автомобиля.
Но TDD от этого не превращается в BDD, как не превратится в конвейер автомеханик от того, что упорядочит процесс сборки автомобиля.
На мой взгляд, не корректно говорить, что BDD включает TDD. BDD — это скорее частный случай TDD, с особым стилем написания тестов, ориентированным на тестирование поведения. Написание спецификаций низкого уровня (те что аналогичны юнит тестам) на RSpec-е это тоже часть BDD, а вы называете это почему-то уже TDD.
На картинке что вы приводили, изображены внешний и внутренний цикл BDD, использующиеся при подходе outside-in, применяемом в BDD. Вы пытаетесь выдать внешний цикл за BDD, а внутренний за TDD, но на самом деле, оба они представляют BDD. По большей части, я согласен с DarthSim, не понятно почему его комментарии минусуют.
На картинке что вы приводили, изображены внешний и внутренний цикл BDD, использующиеся при подходе outside-in, применяемом в BDD. Вы пытаетесь выдать внешний цикл за BDD, а внутренний за TDD, но на самом деле, оба они представляют BDD. По большей части, я согласен с DarthSim, не понятно почему его комментарии минусуют.
Да нет же.
Я не говорил, что BDD — это внешний цикл. Но я согласен, что говорить «BDD включает в себя TDD» — не совсем корректно. Исправляюсь.
Внутренний цикл — это юнит-тестирование (тестирование при помощи спецификаций). Внешний цикл — это тестирование при помощи историй. Все вместе — BDD.
Тестирование при помощи историй оперирует концепциями пользователя, а не кода, и позволяет включить в процесс разработки бизнес-аналитиков. Это, по моему мнению, и является существенным.
Если исключить из BDD внешний цикл, получится TDD.
DarthSim же пытается доказать (если я, конечно, правильно понял его точку зрения), что внешний цикл несущественен, а определяющей чертой BDD является стиль написания юнит-тестов во внутреннем цикле.
PS Я ставлю минус тем комментам, с которыми совершенно не согласен. В карму поставил плюсы.
Я не говорил, что BDD — это внешний цикл. Но я согласен, что говорить «BDD включает в себя TDD» — не совсем корректно. Исправляюсь.
Внутренний цикл — это юнит-тестирование (тестирование при помощи спецификаций). Внешний цикл — это тестирование при помощи историй. Все вместе — BDD.
Тестирование при помощи историй оперирует концепциями пользователя, а не кода, и позволяет включить в процесс разработки бизнес-аналитиков. Это, по моему мнению, и является существенным.
Если исключить из BDD внешний цикл, получится TDD.
DarthSim же пытается доказать (если я, конечно, правильно понял его точку зрения), что внешний цикл несущественен, а определяющей чертой BDD является стиль написания юнит-тестов во внутреннем цикле.
PS Я ставлю минус тем комментам, с которыми совершенно не согласен. В карму поставил плюсы.
Может я не правильно понял, но создалось такое ощущение по вашим предыдущим комментариям.
С тем что вы сейчас говорите, я почти согласен. Но вот мне кажется, что не стоит завязывать внешний цикл на тестирование при помощи именно историй и Cucumber.
Мне думается, это могут быть любые acceptance tests (для Руби — rspec, rspec + capybara/steak), описывающие высокоуровневые фичи (feature) в терминологии заказчика. Т.е. где название теста (плюс всякий сахар типа вложенности контекстов, сценариев и т.д.) как бы создает описание высокоуровневой фичи или целой пользовательской истории. Но, в моем понимании, BDD не требует того, чтобы бизнес аналитик мог читать (или еще хуже писать) код таких тестов (хотя, возможно, существуют и такие радикальные конфессии в BDD). Так как, по-моему, тут основная ценность в том, чтобы увидев, какие тесты внешнего круга падают, а какие работают, заказчик (или бизнес аналитик) мог понять, какие фичи в приложении реализованы, а какие нет. Т.е. чтобы вместо ошибки «падает какой-то ассерт в классе отправки почты», он видел что фича «Отправлять пользователю письмо с при регистрации» не работает. А как код такого теста реализован внутри (Cucumber или хоть читый RSpec), уже вторично.
Поэтому я думаю, что BDD не перестанет быть BDD если вместо Cucumber для тестов внешнего круга использовать даже чистый RSpec. Главное, чтобы эти тесты действительно оставались тестами внешнего круга.
PS: И, действительно, такие acceptance тесты очень похожи на интегральные тесты. Я бы даже сказал, что это что-то вроде интегральных тестов для заказчика (бизнес аналитика).
С тем что вы сейчас говорите, я почти согласен. Но вот мне кажется, что не стоит завязывать внешний цикл на тестирование при помощи именно историй и Cucumber.
Мне думается, это могут быть любые acceptance tests (для Руби — rspec, rspec + capybara/steak), описывающие высокоуровневые фичи (feature) в терминологии заказчика. Т.е. где название теста (плюс всякий сахар типа вложенности контекстов, сценариев и т.д.) как бы создает описание высокоуровневой фичи или целой пользовательской истории. Но, в моем понимании, BDD не требует того, чтобы бизнес аналитик мог читать (или еще хуже писать) код таких тестов (хотя, возможно, существуют и такие радикальные конфессии в BDD). Так как, по-моему, тут основная ценность в том, чтобы увидев, какие тесты внешнего круга падают, а какие работают, заказчик (или бизнес аналитик) мог понять, какие фичи в приложении реализованы, а какие нет. Т.е. чтобы вместо ошибки «падает какой-то ассерт в классе отправки почты», он видел что фича «Отправлять пользователю письмо с при регистрации» не работает. А как код такого теста реализован внутри (Cucumber или хоть читый RSpec), уже вторично.
Поэтому я думаю, что BDD не перестанет быть BDD если вместо Cucumber для тестов внешнего круга использовать даже чистый RSpec. Главное, чтобы эти тесты действительно оставались тестами внешнего круга.
PS: И, действительно, такие acceptance тесты очень похожи на интегральные тесты. Я бы даже сказал, что это что-то вроде интегральных тестов для заказчика (бизнес аналитика).
Но вы согласны, что без внешнего круга BDD превращается в TDD, пускай в новом качестве?
В подтверждение этого утверждения привожу цитату в комменте выше.
В подтверждение этого утверждения привожу цитату в комменте выше.
Хмм, а как быть с разработкой, предположим, gem'ов или бандлов? Имхо тут внешний круг отойдет сам собой без ущерба концепции. Хотя, смотря что понимать под внешним кругом.
И да и нет. Просто сами понимаете, что на практике круг может быть не всегда круглым, и не совсем внешним. Например, может быть вообще только один круг, а может быть три круга или пять кругов. Это противоречит BDD или принципу outside-in? На мой взгляд это все условности. Поэтому может превратиться в TDD, может в недоBDD, а может вообще превратиться в новую методологию.
А определение из книжки, которое вы привели, мне очень нравится: короткое, точное, емкое. А, главное, исходит от создателя(ей) BDD. Предлагаю сосредоточиться на нем, а не на условностях типа фреймворков и всяких там кругов. Фреймворки и круги просто позволяют на практике это быстро понять, попробовать и почуствовать.
А определение из книжки, которое вы привели, мне очень нравится: короткое, точное, емкое. А, главное, исходит от создателя(ей) BDD. Предлагаю сосредоточиться на нем, а не на условностях типа фреймворков и всяких там кругов. Фреймворки и круги просто позволяют на практике это быстро понять, попробовать и почуствовать.
Да я топик особо не читал. Так, увидел начинающийся срач в комментах и решил вписаться.
Если серьезно, я не берусь вас рассуждать, просто выскажу свое мнение: BDD тут и не пахнет. Однако, не все так просто. На мой взгляд, на таких синтетических примерах нельзя однозначно ничего утверждать. Тут можно слишком многое домыслить. Например, если предположить, что автор — единственное заинтересованное лицо (и будущий пользователь, заказчик), а система которую он разрабатывает — это один единственный юнит, то получается что это уже BDD по всем формальным признакам. Я бы даже сказал что при таких условиях, в этом частном случае TDD == BDD. Но это уже какая-то демагогия. А по существу я с вами согласен.
Если серьезно, я не берусь вас рассуждать, просто выскажу свое мнение: BDD тут и не пахнет. Однако, не все так просто. На мой взгляд, на таких синтетических примерах нельзя однозначно ничего утверждать. Тут можно слишком многое домыслить. Например, если предположить, что автор — единственное заинтересованное лицо (и будущий пользователь, заказчик), а система которую он разрабатывает — это один единственный юнит, то получается что это уже BDD по всем формальным признакам. Я бы даже сказал что при таких условиях, в этом частном случае TDD == BDD. Но это уже какая-то демагогия. А по существу я с вами согласен.
Господи-боже нет!) Мы в итоге друг друга не поняли и доказывали дру другу одно и то же с разных концов. Я понял, что Вы считаете, что BDD — это Cucumber и баста, а все прочее — уже «не щитово».
Собственно, непонимание началось после утверждения, что у топиккастера TDD, ибо юнит-тесты.
Собственно, непонимание началось после утверждения, что у топиккастера TDD, ибо юнит-тесты.
Ну так я и утверждаю, что в топике — классический TDD.
Одним из трех основополагающих принципов BDD (та же 121-я страница The RSpec BooK) является «Up-front planning, analysis, and design» (планирование, анализ и проектирование наперед). Топикстартер же честно признался, что когда он начал кодить, он сам еще не знал, что, собственно будет делать его класс. Он проектировал на ходу, то есть по мере разработки.
Так что BDD у топик-стартера нет и в помине (как, видимо, его нет и в статье, на которую вы первоначально сослались). Нет ножек — нет мультиков.: Р
Одним из трех основополагающих принципов BDD (та же 121-я страница The RSpec BooK) является «Up-front planning, analysis, and design» (планирование, анализ и проектирование наперед). Топикстартер же честно признался, что когда он начал кодить, он сам еще не знал, что, собственно будет делать его класс. Он проектировал на ходу, то есть по мере разработки.
Так что BDD у топик-стартера нет и в помине (как, видимо, его нет и в статье, на которую вы первоначально сослались). Нет ножек — нет мультиков.: Р
So allow me to retort! © :)… хотел было написать я, но перечитал таки тесты у топиккастера. Да, там даже по моим меркам как-то не BDD.
А Косс намеренно упростил в статье тесты, чтобы не пугать неокрепшие умы начинающих. Конечно, для проверки поведения нужно было мокать функцию-параметр и проверять вызовы, а не результат (TDD-way).
А Косс намеренно упростил в статье тесты, чтобы не пугать неокрепшие умы начинающих. Конечно, для проверки поведения нужно было мокать функцию-параметр и проверять вызовы, а не результат (TDD-way).
Эх, вот если бы рассмотрел кто-нибудь пример корпоративного приложения, где логики не так много, а данных — море…
Кстати, за холиваром забыл сказать: спасибо за замечательную статью. Когда я впервые вникал в TDD, мне такой статьи очень нехватало.
И вам спасибо за ценный фидбек. Честно говоря не расчитывал на такое внимание, думал топик будет любопытным, но судя по количеству добавивших в избранное тема людям действительно интересна. Когда созреют новые интересные мысли, обязательно напишу еще.
Спасибо за хорошую статью! Просто, но понятно, и задаёт направление для дальнейшего развития. Я сам пока только примеряюсь к юнит-тесированию, и тоже хочу попробовать начать с чего-то такого вот.
Я бы пару тестов объединил в один, например, unitCanTakeDamage и damageTakenReducesUnitHealth. Не понимаю, зачем отдельный первый тест? Он же тестирует только наличие метода takeDamage(), но не его работу. Второй тест проверит и то и другое. Не будет ли разделение на два отдельных теста чрезмерным стремлением затестировать всё подряд? Или именно в этом и заключается TDD? У того же Кента Бека во всех тестах есть assert-ы.
Интересно, что в комментариях больше говорили о тестировании в целом, чем о примере применения тестирования в статье, нет комментариев по делу от гуру TDD. А именно этого просил автор, и хотел бы увидеть и я.
Я бы пару тестов объединил в один, например, unitCanTakeDamage и damageTakenReducesUnitHealth. Не понимаю, зачем отдельный первый тест? Он же тестирует только наличие метода takeDamage(), но не его работу. Второй тест проверит и то и другое. Не будет ли разделение на два отдельных теста чрезмерным стремлением затестировать всё подряд? Или именно в этом и заключается TDD? У того же Кента Бека во всех тестах есть assert-ы.
Интересно, что в комментариях больше говорили о тестировании в целом, чем о примере применения тестирования в статье, нет комментариев по делу от гуру TDD. А именно этого просил автор, и хотел бы увидеть и я.
С другой стороны, тест unitCanTakeDamage тестирует не только наличие метода, но и фиксирует его интерфейс, и если вдруг кто-то добавит ещё один параметр к методу, то тест непременно упадёт, и сразу будет понятно, в чём именно дело. Что довольно ценно, на мой взгляд.
Не понимаю, зачем отдельный первый тест? Он же тестирует только наличие метода takeDamage(), но не его работу. Второй тест проверит и то и другое.
Тесты ведь не только проверяют работу кода, но и являются документацией к нему. В этом смысле наличие отдельного теста, фиксирующего наличие и сигнатуру метода, делает чтение этой «документации» более плавным — сначала вы узнаете, что есть такая фича, потом читаете о ней подробнее.
Это, конечно, не принципиальный момент, я никогда не пишу такие тесты специально, они появляются сами собой «мне тут нужен такой-то метод — напишу тест, теперь этот метод делает такую-то штуку — напишу следующий тест».
Тесты ведь не только проверяют работу кода, но и являются документацией к нему. В этом смысле наличие отдельного теста, фиксирующего наличие и сигнатуру метода, делает чтение этой «документации» более плавным — сначала вы узнаете, что есть такая фича, потом читаете о ней подробнее.
Это, конечно, не принципиальный момент, я никогда не пишу такие тесты специально, они появляются сами собой «мне тут нужен такой-то метод — напишу тест, теперь этот метод делает такую-то штуку — напишу следующий тест».
Нужно уменьшать количество тестируемого. Точнее, нужно выкидывать тесты на очевидные и проверяемые другими тестами данные. Например, не нужно проверять наличие класса и возможность его инициализировать, т.к. без класса отвалятся все остальные тесты, а значит, этот факт уже протестирован. то же и с методами. Если у вас есть хоть 1 тест на проверку поведения метода, тот этот тест уже проверяет его наличие.
Из документации это еще очевиднее. Если в документации появляется описание метода, очевидно, что он существует. Я лично ни в одной доке не видел строчки «этот метод есть».
Если же вы являетесь адептом хардкорного ни-строчки-кода-без-теста TDD, то пишите тесты на наличие, а после написания кода — удаляйте их.
Из документации это еще очевиднее. Если в документации появляется описание метода, очевидно, что он существует. Я лично ни в одной доке не видел строчки «этот метод есть».
Если же вы являетесь адептом хардкорного ни-строчки-кода-без-теста TDD, то пишите тесты на наличие, а после написания кода — удаляйте их.
Sign up to leave a comment.
Простой, но показательный пример использования TDD