Как стать автором
Обновить

Комментарии 48

Раскройте мысль, пожалуйста:)
>1с, tdd, bdd, fluent interface,
Не в обиду 1С кодерам, у меня просто шаблон затрещал
Счетчик людей понявших, что 1С-ники давно перестали быть «вещью в себе» увеличен. Это приятно.
сейчас уже можно сказать, что разработка через тестирование стала стандартом де-факто. Практически в любых вакансиях фигурирует требование к знанию и опыту использования методики TDD и соответствующих инструментов.
Это не так. TDD работает в некоторых частных случаях, но не более.
Очень сильно зависит от области деятельности. В вебе, например, вполне себе часто, в embedded — напротив. Хотя и читал недавно забавную книжку Test Driven Development for Embedded C by James W. Grenning (PragPub), в которой рассказывается как можно использовать TDD в embedded.
Кстати, и как книга? Я её видел и удивился, что такое есть вообще!
В принципе, прочитать стоит, если интересуетесь embedded. Но принципиально нового ничего там не увидел.

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

Отвечу artbear здесь же. Проблема использования TDD в embedded в том, что есть вещи для которых практически невозможно сделать нормальные стабы/моки, т. к. control flow недетерминирован (те же прерывания/события). Проблемы, в общем, схожи с тестированием асинхронных приложений. Для нормального покрытия тестами нужно делать очень много тестов.

Задача по сложности сравнимая с написанием хорошей симуляции соответствующего процессора с обвязкой. Если говорить про life-critical, то там будут делать и аппаратные эмуляторы, т. к. реальное железо сильно отличается от того, что описано в datasheet'ах (достаточно заглянуть в errata чего-нибудь более-менее распространенного и не очень нового).

Вы представляете себе человека, который будет делать тестовую ситуацию с порчей памяти при DMA-передаче память-память, если в этот момент на UART порт «удачно» пришел байт с установленным старшим битом (а заодно все другие вариации, когда этого не произошло)? Я нет. Куда реалистичнее аппаратный симулятор, который будет прогонять большое количество недетерминированных тестов, варьируя параметры.

Но для большинства задач увеличение стоимости разработки на пару-тройку порядков будет мотивацией отказаться от такого серьёзного тестирования, т. к. дешевле перепрошить телевизор/плеер/телефон/whatever в сервис-центре, если вдруг что-то такое случится.
Указанную книгу видел, но не читал, т.к. не работаю с embedded

А в чем проблема использования TDD с embedded?
Оптимизация памяти, ресурсов?
TDD работает в некоторых частных случаях, но не более

Прямо-таки в «некоторых частных случаях»? А в общем случае не работает вообще? Мне кажется, вы что-то перегибаете палку
Много ли вы видели библиотек, более-менее серьезных фреймворков, движков БД, написанных с помощью TDD?
Например, github.com/JetBrains/intellij-community
Не берусь утверждать, насколько тру-TDD используется, но количество авто-тестов большое.
Откуда вы почерпнули, что они используют TDD? Отсутствие тестов означает неиспользование TDD, но не наоборот.

Значительная часть проектов того же Apache покрыта тестами, но нигде не видел использования TDD, т. к. кодом владеет довольно много людей. Что-то похожее на TDD иногда используется при правке багов (сначала делается тест, потом правится баг), но тоже далеко не всегда.
писать авто-тесты на существующий код иногда просто не возможно, ведь код, написанный в обычной парадигме, как правило совершенно тесто-не-пригодный.

Количество тестов позволяет предположить, что TDD имеет место быть.
писать авто-тесты на существующий код иногда просто не возможно, ведь код, написанный в обычной парадигме, как правило совершенно тесто-не-пригодный
Если вы под «обычной парадигмой» понимаете говнокод-спагетти, то его тестами покрывать сложно. Но не стоит называть это «обычной парадигмой». Просто плохое проектирование, безотносительно причин.

Повторюсь, хорошее проектирование не требует использование TDD и использование TDD не гарантирует хорошо спроектированного приложения.

Количество тестов позволяет предположить, что TDD имеет место быть.
Не позволяет, это обращение импликации. Плохое покрытие говорит о неиспользовании TDD, нормальное покрытие не говорит об использовании или неиспользовании TDD. Гораздо вероятнее, что у них просто есть требование написания тестов на новый функционал и регрессионных тестов на баги, такой подход тоже даёт более-менее хорошее покрытие кодовой базы.

philipto, не просветите нас о процессах разработке в JetBrains?
в разных командах в JetBrains процесс может отличаться. Отвечает ведущий разработчик из команды IntelliJ IDEA Николай Чашников: «Команда в целом — не использует TDD. Возможно, отдельные разработчики для отдельных частей кода и используют.»
Спасибо за информацию)
Отсутствие примеров не доказывает принципиальную невозможность. У вас есть более существенные аргументы в подтверждение тезиса о неработоспособности TDD в каких-то (судя по формулировке, их должно быть много, но можно пояснить и на упомянутых выше примерах) конкретных применениях? Почему вы считаете, что там TDD не применимо?
Я бы сказал, что:
TDD НЕ работает в некоторых частных случаях

Например, «тяжело» разработать UI через тестирование.
Для всего остального вполне себе работает.
Я бы еще добавил, что TDD, как правило, не стоит использовать при подготовке прототипа.
Прототип очень часто выкидывается полностью.
Он должен выкидываться. Но с позиции клиента — очень велик соблазн не выкидывать — «оно же уже работает».
Простите, но у меня сложилось впечатление, будто вы не понимаете, что такое TDD. Все же покрывать код тестами и использовать TDD это не одно и то же.
1) Написать падающий тест
2) Написать код, чтобы написанный тест прошел
3) Провести рефакторинг
4) goto 1
Сложно как-то не правильно понимать, что такое TDD.

Кто-то проектирует в голове, кто-то на бумаге, я же проектирую в тестах. Видимо у меня мозг заточен по «Кент Бековски», поэтому TDD прижился.
Подавляющее большинство задач, с которыми мне приходится сталкиваться (разработка всякой разной бизнес-логики), эффективно решается test-first подходом. Главное, чтобы слой логики был отделен от слоя UI.
Чуть дополню, вставив шаг 4 и дополню п.1

1) Написать тест. Убедиться в его падении.
2) Написать код, чтобы написанный тест прошел
3) Провести рефакторинг.
4) Прогнать тесты
5) goto 1
Возможно я ещё не переломил свой мозг как положено, но каждый раз, когда я пытаюсь использовать BDD, это выглядит как-то так:
— пишу тест
— пишу код
— переделываю тест под написанный код.

А так, на самом деле, BDD действительно помогает взять нечто «старое», покрыть тестами, затем написать новое и проверить соответствие.
Писать же сразу с нуля тесты, а потом под них код, у меня не получается совсем (даже если это API, даже если всё кажется чистым и понятным).
Я думаю это из за того, что вы не выполняете предварительную работу
Конечно, я не агитирую за «не-BDD». Возможно, в какой-то мере можно настолько продумывать код заранее, у меня хорошо получается обычно наоборот: черновик логики — декомпозиция — шлифовка — тесты.
BDD, как методология, не предназначена для покрытия существующего кода, но для написания нового. Инструменты, создаваемые для использования в BDD могут использоваться и для написания user stories/specs под имеющийся код, но это уже не BDD. Также как использования какого-нибудь xUnit фреймворка с TDD связано только тем, что в TDD он может применяться.

В BDD код создается под user stories (которые задаются в терминах домена, что не обязательно в TDD) или спецификации (опять же в терминах домена, в котором используется описываемый модуль). Т. е. в терминах BDD вы сначала пишете какой-нибудь сценарий:
Given user with browser without JSESSIONID cookie set
And without X-AUTH cookie set
When he enters to restricted area
Then he receive redirect to authentication page

Потом пишете обвязку, которая из текстовых описаний в секциях Given/When/Then собирает нужные инициализирующие операции (из Given), действия над system under test (из When) и проверку постусловий (из Then).

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

Если вы оказываетесь в ситуации, что написанный код не соответствует user stories, то либо код неверен, либо он решает другую задачу (не соответствует спецификации/user story).
Да, я понимаю эту концепцию и даже применяю её периодически. Только в моём случае это хорошо работает именно как юзер-стори. То есть, если к примеру я разрабатываю некое API и пишу тест на Behat (это под php), то результат удачен только когда я тестирую это «как видит пользователь». Вплоть до того, что мне показалось проще имитировать вызовы браузера (для REST-like API), чем вызовы методов классов. Но если пытаться использовать подобные истории как тест самой логики кода (а не внешних вызовов), то тут у меня всегда ступор. Потому что код нового продукта я меняю очень и очень быстро в процессе написания. И только когда он _уже_ выполняет некую завершенную задачу, я декомпозирую его для соблюдения разных принципов (вроде единой ответственности). И тогда только могу покрывать тестами.
И вот на этом этапе приходится менять и те тесты, которые вроде были предназначены для проверки готового результата. Результат после шлифовки часто отличается (добавляются позитивные, негативные сценарии, параметры одних объектов становятся зависимыми самостоятельными объектами).

Конечно, если идёт работа внутри команды, можно заранее согласовать требования к тому же API, выполнить строго в рамках требований, а затем уже предлагать изменённые варианты. Но в одиночку я так не мыслю.
У меня ситуация аналогичная. Когда требования уже сформулированы (прототип или какая-то реализация уже есть), первичное проектирование сделано, то можно написать спеку/user story и потом писать код. Но либо это уровня функциональных тестов и выше (тогда хорошо идут user stories), либо чётко очерченный модуль, с понятной спецификацией и контрактом (тогда спеку написать не составляет никакого труда, но можно и после кода).

добавляются позитивные, негативные сценарии
Это просто расширение спецификации/user stories, оно абсолютно нормально в рамках BDD.

Вообще с TDD/BDD ещё более-менее нормально в языках с динамической типизацией, но куда хуже в языках со статической. Особенно, когда есть привычка пользоваться нормальной IDE, т. к. при написании теста (или соответствующих правил для Given/When/Then) автодополнение не работает, всё красное. Не комфортно. Поэтому часто сначала пишется stub-класс, потом тест, потом заполняется stub, что уже не очень соответствует канонам TDD.
Возможно в этом случае должны помогать интерфейсы (которые сами по себе уже дублируют идею BDD)
Вы ведь все равно запускаете код, во время написания, для проверки? Или сразу пишете целиком? В любом случае, конкретно TDD служит не столько для тестирования, сколько для описания будущего интерфейса, чтобы не двигаться вслепую, а видеть цель. Совершенно необязательно писать сразу полноценный тест, достаточно контрольные точки расставить.
Почему не написать так:
Ожидаем.Что(5).НеРавно(7);

По-моему, красивее чем
Ожидаем.Что(5).Не_().Равно(7);
Потому что почти для каждого утверждения есть свой антипод и тогда нужно будет реализовывать в 2 раза больше утверждений. В текущей реализации любое утверждение можно инвертировать и дублировать ничего не нужно.
Понял, спасибо!
Разобрал бинарники на исходники github.com/artbear/xUnitFor1C_2.git
Теперь можно посмотреть на исходники и увидеть, как в 1С можно писать хороший код :)
Вместо Не_() пишите Нет().
Рассматривал этот вариант. На мой вкус:
Ожидаем.Что(5).Не_().Равно(7);

лучше чем:
Ожидаем.Что(5).Нет().Равно(7);
Ну тогда вместо Не_() пишем ОтнюдьНет(). Смущает Не_ -подчеркивание — это обычно какая то мусорная временная переменная/функция. ИМХО.
Интересное предложение.
Мне стало нравиться
Ожидаем.Что(5).СовсемНе().Равно(7); 


или еще лучше
Ожидаем.Что(5).ТочноНе().Равно(7);
Ожидаем.Что(5).ЭтоНе().Равно(7);


А так смысл сохраняется)
ЭтоНе() выглядит интересно, спасибо! Обязательно попробую применить совет.
Нужно будет только по другому обыграть другие зарезервированные слова (Истина, Ложь, Null, Неопределено) в утверждениях, возможно это окажется легче.
Жду следующую статью с нетерпением)
Мы еще обсуждали «ВащеНе» и «НиРазуНе»
Мне тоже Не_() не нравится. Может вместо Не_() использовать Отрицание()?
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации