Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Программирование Новый топик    Ответить
Топик располагается на нескольких страницах: Ctrl  назад   1 2 3 4 [5] 6   вперед  Ctrl      все
 Re: Про TDD  [new]
softwarer
Member

Откуда: 127.0.0.1
Сообщений: 54847
Блог
ViPRos
уровень заказчиков таких пещерный .... т.е. я не против ТДД или там ПДД, но только не с этими ОАО и ГАИ ....

Знаешь, я убеждён в одной вещи. Заказчик - источник хаоса. Просто потому, что это не профессия. Нет, не сложилось такой профессии как "грамотный заказчик программного продукта". А раз заказчик - источник хаоса, значит где-то между ним и разработчиком должна быть такая точка, где хаос останавливается и дальше - порядок. Эту точку можно называть - РП, аналитик, как угодно, не суть, но она должна быть. В команде разработки должен быть человек, одна из основных обязанностей которого - "хаос кончается на мне". И вся остальная команда работает с этим человеком так, словно он - тот самый идеальный, непещерный заказчик.

Согласен, что это редкость. Но я убеждён, что надо к этому стремиться и строить именно так. Да, нередко хаос доходит до программиста. Даже есть программисты, которые убеждены, что этот хаос - естественное и здоровое состояние, и их профессионализм определяется умением в нём работать. Тут я с тобой согласен... пещерные люди.
21 авг 15, 20:44    [18053467]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
softwarer
F#
Это надо обосновать - почему тестирование только отдельных кусков херит всю идею.

Это обосновали почти пятьдесят лет назад, когда провели исследование и обнаружили, что основная тяжесть ошибок в программах приходится не на "отдельные куски", а на их взаимодействие.


Из этого не следует что тестирование отдельных кусков "херит всю идею". Каждое конкретное взаимодействие заключено в каждом конкретном куске. Это раз. Во-вторых, если моя жизнь не состоит из сплошного кипячения чайника, это не значит, что электрочайник это плохая идея.

F#
Например в электрический чайник я наливаю воду вручную, но дальше он кипятит чай самостоятельно. Если он облегчает мне часть работы почему бы его не использовать?

В самом деле. Пару месяцев назад моя жена купила электрический чайник. У него оказалась клёвая фича - после того, как он вскипятил воду, он не отщёлкивает кнопку, а просто термореле отключает спираль. Минут через пятнадцать он снова включается и снова доводит воду до кипения. И так далее. Вы берёте его, тестируете кусок, вставляете в квартиру - и уезжаете, рассчитывая к возвращению иметь чайник кипячёной воды. А приезжая, обнаруживаете в лучшем случае пустой чайник, а в худшем - пожар. Профит кусочного тестирования!


Заметим что тут чайник в качестве аналогии переехал в качество тестируемого объекта из примера кусочной автоматизации.

- Я подразумевал НЕ кусочное тестирование, а кусочную автоматицацию. Не автоматизировать ту часть работы, которую по каким-то причинам сделать трудно - тестировать вручную.

- Если бы было протестировано отщелкивание кнопки отдельно от остального чайника, возможно этой ошибки не было бы.

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


Замечательный способ сделать огромную кучу работы, которая так ничего и не гарантирует. Всего лишь вместо вопроса "правильно ли один вывод подсовывается в интерфейс" останется вопрос "правильно ли другой вывод подсовывается в интерфейс".


Мне кажется, иногда модули делают что-то другое кроме подсовывания выводов.

По моему опыту, в любом случае что-то не будет гарантироваться. Или из-за того, что тестируется не все автоматически, или из за того, что тесты слишком медленные и их будут запускать редко.

Вот статья на эту тему.

Интересно было бы узнать ваш опыт - сколько тестов, как часто они запускаются, насколько часто падение тестов ловит баги, насколько много ложных срабатываний, как обрабатываются нестабильные тесты.
26 авг 15, 10:17    [18068730]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
softwarer
White Owl
то в духе TDD будет сначала исправить существующий тест (или написать дополнительный тест специально для этого параметра), убедиться что этот тест падает, и только потом начать изменять метод.

Что-то мне подсказывает, что "этот тест" будет не "падать", а "не компилироваться" :)


Вообще, TDD, началось на Смолтоке, насколько я помню.
26 авг 15, 10:20    [18068742]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
petalvik
softwarer
Это снова изменение требований.

Нет, требования не изменились, остались прежними. Просто автор теста не знал досконально поведение вызываемого кода/используемой библиотеки.


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

Знать это поведение он может либо в том случае, когда уже писал код с ней (сперва - код!), либо проведено проектирование с тщательным изучением всего и вся (сперва - проект!).


Не обязаетельно всего и вся. Можно изучать кусочно, постепенно уточняя требования.

Тест в обоих случаях не первый.


Тест никогда не первый вообще. Он просто идет перед production кодом.

В XP, например, используется [url=]spikes[/url] - для изучения чего-то пишется прототип, можно без тестов и в одиночку. Который потом можно выкинуть и полученные знания использовать в production code, написанном в паре и по TDD.
26 авг 15, 10:32    [18068820]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
F#
.. используется spikes - ...
26 авг 15, 10:34    [18068829]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
softwarer
Member

Откуда: 127.0.0.1
Сообщений: 54847
Блог
F#
Из этого не следует что тестирование отдельных кусков "херит всю идею". Каждое конкретное взаимодействие заключено в каждом конкретном куске. Это раз.

Это демагогия примерно того же стиля, что и "тестирование всей программы в совокупности - это тоже вид юнит-теста".

Нет. Каждое конкретное взаимодействие не заключено в каждом конкретном куске кода. Если понимать под куском тот "юнит", который в тестировании - это просто очевидно. Если начать растягивать понимание "куска" - то и тогда ни одно взаимодействие вида "А формирует параметры и передаёт Б, тот их обрабатывает и передаёт В, тот их анализирует и передаёт половину Г, а другую Д" ни одним "куском" не покрывается. И сколько северный варвар ни будет тестировать каждый из этих кусков поотдельности, он не добьётся уверенности в том, что они сработают вместе - ровно так же, как можно до посинения тестировать кирпичи и раствор, а стена всё равно развалится.

F#
Во-вторых, если моя жизнь не состоит из сплошного кипячения чайника, это не значит, что электрочайник это плохая идея.

Электрочайник - это хорошая идея. А вот кусочное тестирование - плохая.

F#
- Если бы было протестировано отщелкивание кнопки отдельно от остального чайника, возможно этой ошибки не было бы.

А почему Вы считаете это ошибкой? Я полагаю, это фича. И тестирование покажет, что она работает.

F#
Я знаю только, что при покупке обычно делают какие-то упрощения. Например, включают и проверяют, теплая ли спираль.

В том и проблема. Покупатель купил чайник, провёл кусочный тест и доволен. А существенная особенность работы (автовключение) осталась за кадром, и поэтому взаимодействие чайника с остальной системой привело к пожару. И никакой кусочный тест предотвратить эту проблему не способен - потому что человек в здравом уме не будет придумывать тесты на "а не включается ли чайник сам", "а не шлёт ли он по ночам шпионские фотоснимки в Пентагон", "а не вылезают ли из него по ночам инопланетяне". Чтобы адекватно предотвратить эту проблему, нужно тестировать систему в совокупности - то есть, в нашей аналогии, не оставлять чайник без присмотра до тех пор, пока присмотр не покажет, что "чайник, оставленный на 8-16-24-48 часов, работает ожидаемым образом, не требуя вмешательства".

F#
Мне кажется, иногда модули делают что-то другое кроме подсовывания выводов.

Иногда, возможно, описанный способ и будет адекватным решением. В данном случае.. соотношение цена-качества кажется мне удручающим.

F#
По моему опыту, в любом случае что-то не будет гарантироваться. Или из-за того, что тестируется не все автоматически, или из за того, что тесты слишком медленные и их будут запускать редко.

Что-то гарантироваться, конечно, не будет - поскольку никогда не будет стопроцентного покрытия итп. Медленные тесты имхо не являются проблемой - поскольку их можно запускать и гонять автоматически, а любой вопрос "медленности" решается масштабированием. Грубо говоря, у меня в конторе штук тридцать-сорок девелоперских компов. То есть, условно, собрав ночной билд в час ночи, скрипт может запустить тесты примерно на восемь-десять суток суммарной продолжительностью, и к моменту прихода разработчиков результаты будут готовы. Думаете, не хватит?

Это не значит, что я считаю, что все тесты надо гонять через интерфейс. Я считаю, что через интерфейс надо гонять достаточное количество тестов для того, чтобы испытывать уверенность в том, что и "другие варианты", протестированные на более слабом уровне, сработают и через интерфейс тоже.

F#
Вот статья на эту тему.

Если вкратце, то я с ней не согласен. При этом вполне допускаю, что она разумна в тех условиях, для которых писалась. Скажем, условно, если проектом является разработка Chrome, то вполне очевидно, что подавляющее большинство тестов не стоит пускать через интерфейс. Грубо говоря, через интерфейс надо протестировать, что "javascript в html запускается и приводит к нужным видимым изменениям", но при этом незачем тестировать через интерфейс правильность работы каждой фичи javascript-движка.

F#
Интересно было бы узнать ваш опыт - сколько тестов, как часто они запускаются, насколько часто падение тестов ловит баги, насколько много ложных срабатываний, как обрабатываются нестабильные тесты.

Мне трудно обобщить, потому что последние лет десять я в разной степени внедряю автоматическое тестирование везде, где работаю - с разными приложениями, подходами, разной степенью покрытия итп.

Сколько тестов - вопрос практически бессмысленный. Просто для примера, на одном из проектов тестеры размахали сценарий, который шёл часа полтора - он был оформлен как один тест, но включал в себя добрую половину функциональности приложения. Я тогда на них зверски ругался, потому что, конечно, удобнее было бы иметь его в виде нескольких десятков более мелких, но они ответили - это основной бизнес-процесс заказчика, и мы хотим быть уверенными, что пользователь, севший за компьютер, сможет пройти его от начала до конца со всеми закоулками без единого сбоя, скажем без Out of memory в середине работы. Ответить мне оказалось нечего - собственно, это тот же самый пример про чайник. А в терминах юнит-тестов... этот единственный сценарий, наверное, равнялся нескольким тысячам.

Как часто запускается... в том самом наиболее покрытом и тяжёлом проекте полное тестирование запускалось где-то от одного до пяти раз в неделю, ну а выборочные - невозможно посчитать. Задним числом я думаю, что надо было бы внедрить ночные сборки и автоматический прогон тестов, но этого не было. В принципе, расклад был следующим:

Когда проект начинался, я настоял на недельных итерациях (руководство продавливало ежемесячные). В пятницу вечером выпускался release candidate, дальше в течение недели его мучили тестировщики и он шёл на production. Я внедрил автоматическое тестирование сначала локально в рамках разработки, потом, когда оно дошло до удовлетворительного состояния, подключили тестировщиков. Технология вышла такой: release candidate собирался в пятницу утром, прогонялось полное тестирование. Если оно находило проблемы - мелкое решалось на месте, крупное откатывалось, в общем в пятницу вечером гарантированно собирался release candidate, проходящий существующие тесты (старые и дописанные разработчиками для новых фич). В понедельник за него брались тестировщики, гоняли руками и писали тесты (новые либо дописывали и расцвечивали тесты, начатые разработчиками). Обычно, где-то к среде они давали добро на его выпуск в production и переключались на другие проекты. Если тестировщики находили что-то, что нужно было исправить до выпуска релиза - соответственно, выпускался новый кандидат и ещё раз прогонялось полное тестирование.

Насколько часто падение тестов ловит баги... вопрос ставит меня в тупик. Скажу так: я считаю ЧП случай, когда падение теста не ловит багу :) Собственно, я полагаю, что любое падение теста ловит багу либо в приложении, либо в тесте (который тоже часть приложения). Если вопрос в том, как часто ошибку ловят тесты, а как часто - другие источники... зависит от покрытия, конечно. В тех частях приложений, которые я полагаю адекватно покрытыми тестами, "проскользнувшие баги" находятся редко. Бывает в основном на начальной стадии, когда набирается опыт "что именно и как вообще надо тестировать в этой задаче".

Нестабильные тесты.... с такой проблемой я столкнулся только при использовании Selenium. Cразу скажу, при всей замечательности этого продукта использовать его "из коробки" практически невозможно. Я быстро пришёл к необходимости написать к нему оболочку, использующий его фреймворк, который предоставлял бы разработчику вменяемый и доступный к использованию интерфейс. Так вот, нестабильные тесты были следствием таких проблем, как

- проблемы самого Selenium-а (текущей версии)
- проблемы браузера (текущей версии)
- проблемы взаимодействия текущей версии Selenium-а с текущей версией браузера
- проблемы моего фреймворка-оболочки.

Особенно в этом смысле радовал Firefox, после обновления которого регулярно начинали валиться здоровые рабочие тесты. Соответственно, решалось примерно так:

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

Здесь, конечно, подразумевается не ситуция "приложение валится в новой версии браузера", а "приложение в этой версии браузера нормально работает, а вот тесты по какой-то причине стабильно либо нестабильно валятся".
26 авг 15, 13:41    [18070219]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
softwarer
Member

Откуда: 127.0.0.1
Сообщений: 54847
Блог
Короче говоря, если кому-то таки интересно про TDD :) Как раз нарисовался маленький пример, как я его понимаю.

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

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

Появляется требование (вернее, моё желание): проверить, что приложение адекватно поведёт себя, если в том или ином его месте случится неожиданная ошибка. Адекватное поведение - как минимум, означает, что после этого приложение сохранит способность обработать следующие вызовы и дать на них верные ответы.

Для этого я вставляю в клиентскую библиотеку новую команду (simulateError), вставляю в тесты её вызовы с теми параметрами, которые собираюсь использовать. Тестирование начинает ожидаемо сигнализировать о проблемах (в основном ответ сервера "команда simulate error не распознана"). Дальше я дописываю в сервер код, который по приходу этой команды взводит флажки вида "имитируй ошибку там", "имитируй ошибку здесь" итп. Соответственно, тестирование начинает падать по-крупному (довожу до того, что сервисное приложение перестаёт принимать команды вообще из-за падения одного из потоков).

Ну и наконец, вот сейчас начну дописывать приложение так, чтобы оно выдерживало подобные удары.
26 авг 15, 14:34    [18070537]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
softwarer
добьётся уверенности в том, что они сработают вместе - ровно так же, как можно до посинения тестировать кирпичи и раствор, а стена всё равно развалится.


Кирпичи, раствор и детальный чертеж постройки (т.к. у нас робот который собирает здание).


F#
Во-вторых, если моя жизнь не состоит из сплошного кипячения чайника, это не значит, что электрочайник это плохая идея.

Электрочайник - это хорошая идея. А вот кусочное тестирование - плохая.


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

F#
- Если бы было протестировано отщелкивание кнопки отдельно от остального чайника, возможно этой ошибки не было бы.

А почему Вы считаете это ошибкой? Я полагаю, это фича. И тестирование покажет, что она работает.


Если это фича - тогда чем вы недовольны?



В том и проблема. Покупатель купил чайник, провёл кусочный тест и доволен. А существенная особенность работы (автовключение) осталась за кадром, и поэтому взаимодействие чайника с остальной системой привело к пожару.


Начните тестировать каждый чайник при покупке с водой - или вы это так уже делаете? А если нет, то почему?

И никакой кусочный тест предотвратить эту проблему не способен - потому что человек в здравом уме не будет придумывать тесты на "а не включается ли чайник сам", "а не шлёт ли он по ночам шпионские фотоснимки в Пентагон", "а не вылезают ли из него по ночам инопланетяне". Чтобы адекватно предотвратить эту проблему, нужно тестировать систему в совокупности - то есть, в нашей аналогии, не оставлять чайник без присмотра до тех пор, пока присмотр не покажет, что "чайник, оставленный на 8-16-24-48 часов, работает ожидаемым образом, не требуя вмешательства".


Никто не спорит с необходимостью тестировать продукт целиком. Вопрос в том, надо ли это автоматизировать и насколько сильно.

То есть, условно, собрав ночной билд в час ночи, скрипт может запустить тесты примерно на восемь-десять суток суммарной продолжительностью, и к моменту прихода разработчиков результаты будут готовы. Думаете, не хватит?


Я бы предпочел, чтобы код, который рушит тесты просто не прошел gated check-in. Не хотелось бы, чтобы он был раз в 8-9 суток.
Еще хотелось бы чтобы упавший тест как можно быстрее показал, где именно произошла ошибка.

Это не значит, что я считаю, что все тесты надо гонять через интерфейс. Я считаю, что через интерфейс надо гонять достаточное количество тестов для того, чтобы испытывать уверенность в том, что и "другие варианты", протестированные на более слабом уровне, сработают и через интерфейс тоже.


Я тоже за это. Плюс еще цена.

Когда проект начинался, я настоял на недельных итерациях (руководство продавливало ежемесячные).


Спасибо за рассказ, интересно.

Если вопрос в том, как часто ошибку ловят тесты, а как часто - другие источники... зависит от покрытия, конечно. В тех частях приложений, которые я полагаю адекватно покрытыми тестами, "проскользнувшие баги" находятся редко. Бывает в основном на начальной стадии, когда набирается опыт "что именно и как вообще надо тестировать в этой задаче".


Если тесты запускаются реже, чем каждый коммит, не бывает ли такого, что разработчик A внес правильное изменение, но забыл исправить и проверить тест Б. После чего прошло несколько дней и при прогоне всех тестов выявляется что тест Б красный после чего приходится исследовать причину и оказывается что софт рабочий, но надо выискивать где именно поправить тест. В результате:
1) Отсутствует уверенность в том, где баги есть, а где их нет
2) На падение тестов начинают смотреть сквозь пальцы.
26 авг 15, 15:00    [18070697]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
White Owl
Member

Откуда:
Сообщений: 12339
F#
softwarer
пропущено...

Что-то мне подсказывает, что "этот тест" будет не "падать", а "не компилироваться" :)


Вообще, TDD, началось на Смолтоке, насколько я помню.
Вообще-то, TDD началось на java.
26 авг 15, 15:45    [18071070]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
softwarer
Member

Откуда: 127.0.0.1
Сообщений: 54847
Блог
F#
Кирпичи, раствор и детальный чертеж постройки (т.к. у нас робот который собирает здание)

Чтобы протестировать "детальный чертёж постройки", надо выйти за пределы юнит-тестирования.

F#
Перечитайте ваше собственно сообщение - вы аргументировали бесполезность юнит тестирования тем,

Уже неверно. Я аргументировал "почему тестирование только отдельных кусков херит всю идею" (с) 17876513. То есть обосновываю, что в общем случае "одних юнит-тестов" принципиально и категорически недостаточно. Попутно я утверждаю, что юнит-тесты - не являются самыми важными, и упор на них основного внимания является ошибкой.

F#
Даже если это так, то оптимизировать поиск меньшего количества багов тоже может быть полезно.

Безусловно. Но здесь надо соразмерять цели и средства, и я прихожу к выводу, что большая часть программного кода "обычного приложения" в юнит-тестировании не нуждается и более того, оно ей вредит (как минимум, напрасной трудоёмкостью).

В качестве условного примера - давайте предположим, что наше приложение должно позволить пользователю ввести две даты и если первая больше второй, зажечь красную лампочку. Приложение реализовано в виде формы, создаваемой средствами какой-нибудь стандартной библиотеки, и контроллера, сравнивающего даты и возвращающего -1, 0 или 1.

В такой ситуации тест через интерфейс имхо обязателен и эффективен. Во-первых, он позволяет проверить работоспособность двух основных интересующих пользователя сценариев: "лампочка зажглась, когда нужно" и "лампочка не зажглась, когда не нужно". Во-вторых, с минимальными усилиями проверяются, а то и вовсе случайно вскрываются мелкие проблемные ситуации, например: как приложение работает, когда одна из дат введена, а другая - нет? Как приложение работает, если дата введена в виде 1.1.15? А горит ли лампочка, если пользователь ввёл день и месяц, а год ещё не успел? Итп. Как ни странно, именно так вскрывается уйма проблем, которые реально мешают пользователю. Ну например: допустим, всё работало. Потом вышла новая версия библиотеки, или наш программист приписал новую крутую фичу - подстановку во вторую дату года из первой, если он не указан. Он её протестировал, всё хорошо и он доволен жизнью - а тест показывает, что пользователь вводит первую дату "01.01.2015", начинает вводить вторую "29.02" - и внезапно вываливается идиотское сообщение "2015-й год невисокосный, введённая дата неверна".

Давайте теперь рассмотрим варианты. Юнит-тест контроллера, может быть, и не помешает, но и не сказать, что поможет. До тех пор, пока мы не начнём развивать его в нечто крутое, умеющее сравнивать юлианские, григорианские, японские, еврейские и прочие календари, смысла в нём практически нет. Ошибку он почти наверняка не найдёт никогда, даже если найдёт - её вес будет мал, а те случаи, которые таки надо проверить - несложно вставить и в интерфейсный тест и протестировать в боевых условиях в совокупности.

А самое интересное - взаимодействие. Да, Вы можете, как предлагаете, написать заглушку, которая будет имитировать интерфейс. Да, Вы можете через неё подать введённые даты и проверить, появляется ли true в поле "горит лампочка". Да, возможно, Вам даже удастся сделать тесты на моменты типа отличия дат 1.1.15 и 01.01.2015. Но во-первых, писать такую заглушку - нехилая трудоёмкость (на фоне общей для этого приложения и его тестов). Во-вторых, когда программист припишет свою крутую фичу - заглушку придётся модифицировать, чтобы она её учитывала. А в-третьих - багу с "дата неверна" заглушка всё равно не отловит! Просто потому, что её автор - в отличие от автора визуальной библиотеки - эту ситуацию не проверяет или проверяет иначе. То есть, с одной стороны потратили кучу сил, с другой - бага таки доехала до пользователя.

Поэтому я и полагаю, что надо соразмерять широту отлавливаемых проблем, усилия на написание теста и усилия на его дальнейшее сопровождение. И в том, что касается юнит-тестов - я считаю, что ими стоит покрывать более-менее стандартные, переиспользуемые модули. Те, в которых рано или поздно возникнут все кривые ситуации, какие только могут быть. Если же код прикладной, используется один раз по месту, обкладывать его юнит-тестами - сизифов труд. То, что в нём работает "маршрут, которым он используется в приложении" - покажет интегральный тест, а никакие другие в нём и не нужны - этот код будет выброшен либо переработан (вместе с тестами) в тот самый момент, когда эти другие маршруты потребуются.

F#
Если это фича - тогда чем вы недовольны?

Я недоволен? Я как раз доволен.

F#
Начните тестировать каждый чайник при покупке с водой - или вы это так уже делаете? А если нет, то почему?

Так и тест с водой не избавит от описанного пожара.

Я вроде бы описал, как делаю - тестирую в реальных условиях. То есть приношу в свою квартиру и гоняю под присмотром. Так что если этот чайник, например, выбивает мне пробки и размораживает холодильник - я это узнаю. В ответном слове предлагаю рассказать, как Вы это узнаете при кусочном тестировании в магазине :)

F#
Никто не спорит с необходимостью тестировать продукт целиком. Вопрос в том, надо ли это автоматизировать и насколько сильно.

(Пожимая плечами) Надо ли автоматизировать основную и наиболее важную часть тестирования? Этот вопрос эквивалентен вопросу "надо ли автоматизировать тестирование вообще". Мой ответ - уверенное "да". То есть даже при наличии тестировщиков, потенциально готовых руками гонять регрессии, я полагаю это неэффективным.

F#
Я бы предпочел, чтобы код, который рушит тесты просто не прошел gated check-in. Не хотелось бы, чтобы он был раз в 8-9 суток.

В первую очередь я бы хотел, чтобы "код, который рушит" встретился с "тестом, который он обрушит" прежде, чем с пользователем. У юнит-тестов с этим большие проблемы. Когда эта задача решена - можно говорить о желаемой скорости обратной связи.

F#
Еще хотелось бы чтобы упавший тест как можно быстрее показал, где именно произошла ошибка.

Это несерьёзно. Где именно произошла ошибка - и так видно по логу и/или стектрейсу. Почему она произошла - в мало-мальски нетривиальных случаях придётся садиться в отладчик и смотреть. В любом случае, если тест прошёл ассерт номер N и свалился на ассерте N+1 - работа скорее всего начнётся с "поставить брейкпоинт на N-й ассерт и начать разбираться", и от формы и размера теста это практически не зависит.

F#
Если тесты запускаются реже, чем каждый коммит, не бывает ли такого, что ..... В результате:
1) Отсутствует уверенность в том, где баги есть, а где их нет
2) На падение тестов начинают смотреть сквозь пальцы.

Как минимум я никогда не буду смотреть на падение тестов сквозь пальцы. Одна из причин "временного исключения нестабильных тестов", о котором я говорил - как раз в том, что "тесты должны быть зелёными".

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

А бывает ли такое... я не совсем понял вопрос, попробую ответить так. Если речь идёт об области ответственности программиста А - его модуле, его тестах итп - то, в общем, его дело, что и как запускать, от него ожидается проверенный результат. Тесты перед билдом подстрахуют от его забывчивости, но сама по себе ситуация маловероятная. Не скажу, бывало такое или нет, не помню. Может, когда и было.

Если речь о том, что изменение программиста А обваливает какие-то тесты в зоне ответственности программиста Б - то тут в любом случае надо разбираться. Вовсе не факт, что это ошибка А, что она в модуле А и что исправлять должен именно А. Но вообще тут возможны два случая:

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

б) Ошибка далеко, срабатывает какая-то мерзкая нетривиальная бяка. В этот момент я возношу хвалу своей предусмотрительности за то, что тесты перед билдом не пропустили её к пользователю. При этом отмечу, что никакие "быстрые автотесты в момент коммита" отловить такую заведомо не способны.
26 авг 15, 16:09    [18071291]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
White Owl
I often hear, "Of course. How else could you program?"

ну, да.
26 авг 15, 16:51    [18071625]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
White Owl
Member

Откуда:
Сообщений: 12339
F#
White Owl
Вообще-то, TDD началось на java.


Википедия ссылается на

http://www.quora.com/Why-does-Kent-Beck-refer-to-the-rediscovery-of-test-driven-development

The original description of TDD was in an ancient book about programming. It said you take the input tape, manually type in the output tape you expect, then program until the actual output tape matches the expected output. After I'd written the first xUnit framework in Smalltalk I remembered reading this and tried it out. That was the origin of TDD for me. When describing TDD to older programmers, I often hear, "Of course. How else could you program?" Therefore I refer to my role as "rediscovering" TDD.
Забавно.... Ну Ок.
26 авг 15, 18:20    [18072275]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
softwarer
В качестве условного примера - давайте предположим, что наше приложение должно позволить пользователю ввести две даты и если первая больше второй, зажечь красную лампочку. Приложение реализовано в виде формы, создаваемой средствами какой-нибудь стандартной библиотеки, и контроллера, сравнивающего даты и возвращающего -1, 0 или 1.
В такой ситуации тест через интерфейс имхо обязателен и эффективен.


С моей точки зрения, ответ на этот вопрос зависит от того, сколько будет стоить поддержка UI теста. Если фреймворк такой, что это дорого и неудобно, вполне возможно обойтись и юнит тестом. Так же интересно знать во скольких еще местах используется этот контроллер. UI тесты для всех них будут зазря гонять одно и то же.

Давайте теперь рассмотрим варианты. Юнит-тест контроллера, может быть, и не помешает, но и не сказать, что поможет. До тех пор, пока мы не начнём развивать его в нечто крутое, умеющее сравнивать юлианские, григорианские, японские, еврейские и прочие календари, смысла в нём практически нет.


Во-первых, мы будем знать, что контроллер уж точно работает.
Во-вторых, есть масса идиотских ошибок/описок которые он будет вылавливать и так.

Можно также сделать тест на viewmodel и focused integration tests на view.

Если End2End написать достаточно просто, то конечно, можно начать и с него.

А самое интересное - взаимодействие. Да, Вы можете, как предлагаете, написать заглушку, которая будет имитировать интерфейс. Да, Вы можете через неё подать введённые даты и проверить, появляется ли true в поле "горит лампочка". Да, возможно, Вам даже удастся сделать тесты на моменты типа отличия дат 1.1.15 и 01.01.2015. Но во-первых, писать такую заглушку - нехилая трудоёмкость (на фоне общей для этого приложения и его тестов).


Как только мы начнем переиспользовать преобразование строки в дату, эти отдельные тесты начнут окупаться. Нам не надо будет в каждом конкретном случае проверять как действует 1.1.12 и 01.01.2015 - мы просто будем знать, что преобразование дат работает как положено и не тестировать UI framework во всех местах, где он применяется.

Во-вторых, когда программист припишет свою крутую фичу - заглушку придётся модифицировать, чтобы она её учитывала. А в-третьих - багу с "дата неверна" заглушка всё равно не отловит! Просто потому, что её автор - в отличие от автора визуальной библиотеки - эту ситуацию не проверяет или проверяет иначе. То есть, с одной стороны потратили кучу сил, с другой - бага таки доехала до пользователя.


Для тестирования таких ситуаций, наверное, можно использовать интеграционные тесты например пристегнув временное хранилище. Хотя по моему опыту такие штуки (связанные с UI в динамике и не отловленные при первом ручном прогоне) скорее находят при exploratory тестировании просто потому, что они обычно находятся вдали от основных сценариев.

Поэтому я и полагаю, что надо соразмерять широту отлавливаемых проблем, усилия на написание теста и усилия на его дальнейшее сопровождение.


Имхо, если бизнес-логика смоделированна в терминах близких к предметной области, то после разработки бизнес объектов получается достаточно устойчивый набор модулей для которого просто писать тестовые сценарии, причем как при подготовке тестовых предусловий, так и для проверки результатов. Поэтому писать Unit и интеграционные тесты как правило проще. Программная модель UI обычно перегружена несущественными для теста подробностями и пользоваться ей напрямую сложнее и усилий требуется больше.

Я вроде бы описал, как делаю - тестирую в реальных условиях. То есть приношу в свою квартиру и гоняю под присмотром. Так что если этот чайник, например, выбивает мне пробки и размораживает холодильник - я это узнаю. В ответном слове предлагаю рассказать, как Вы это узнаете при кусочном тестировании в магазине :)


Я в магазине это не узнаю, просто покупаю и несу домой - а дальше test in production :)

В первую очередь я бы хотел, чтобы "код, который рушит" встретился с "тестом, который он обрушит" прежде, чем с пользователем. У юнит-тестов с этим большие проблемы. Когда эта задача решена - можно говорить о желаемой скорости обратной связи.


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

С другой стороны, если сделать UI очень тонким, то вероятность сделать в нем ошибку сильно уменьшается. Особенно сделать регрессию. Например после связывания кнопки "Назад" с командой "Назад" маловероятно, что ее кто-то поменяет на "Вперед".

Возможно вам встречалось больше кода, связаного именно с логикой пользовательского интерфейса, чем мне.

Это несерьёзно. Где именно произошла ошибка - и так видно по логу и/или стектрейсу.


Обычно по стектрейсу видно место где последствия ошибки обнаружились. Это может быть далеко от места, где находится ошибочный код.

Почему она произошла - в мало-мальски нетривиальных случаях придётся садиться в отладчик и смотреть. В любом случае, если тест прошёл ассерт номер N и свалился на ассерте N+1 - работа скорее всего начнётся с "поставить брейкпоинт на N-й ассерт и начать разбираться", и от формы и размера теста это практически не зависит.


Зависит. Если есть включает в себя много последовательных шагов, то, очевидно, ошибку найти труднее чем если там 1 шаг с подготовленными данными. Хороший юнит тест не валится, если ошибка произошла в другом юните. А так как их можно запускать очень часто (посмотрите, например, ролик на http://www.ncrunch.net/ - там прямо в процессе написания, они гоняются фоном) и они детально показывают какие ожидания не оправдались, то иногда не надо даже отладки - просто сопоставить, что делал недавно и какой тест упал.

Как минимум я никогда не буду смотреть на падение тестов сквозь пальцы. Одна из причин "временного исключения нестабильных тестов", о котором я говорил - как раз в том, что "тесты должны быть зелёными".

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


Тут мне видится противоречие - либо мы не исключаем нестабильные тесты и не говорим что 100% гарантии не может дать никто, либо таки мы немного беременны. И еще надо запускать все тесты перед каждым изменением - иначе в промежутке междц запуском мы тоже не можем быть уверены.

В-общем, вашу точку я понимаю, но сам склоняюсь, скорее к тестовой пирамиде - немного End-to-end тестов с основными сценариями, некоторое количество integration и много детальных unit - как описано у Фаулера и Шора.
27 авг 15, 00:26    [18073942]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
White Owl
Забавно.... Ну Ок.


Закономерно - в динамических языках нет проверки типов при компиляции, которая ловит много глупых ошибок, при этом цикл red-green-refactor может быть гораздо короче (из за того же отсутствия компиляции).
27 авг 15, 00:29    [18073954]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
softwarer
Member

Откуда: 127.0.0.1
Сообщений: 54847
Блог
F#
С моей точки зрения, ответ на этот вопрос зависит от того, сколько будет стоить поддержка UI теста.

Пожму плечами.

public class ДвеДаты extends AbstractWebTest {
  @Test public void Лампочка() {
    Selenium.inputString('date-from', '01.01.2000');
    Selenium.inputString('date-to', '01.01.2001');
    assertTrue("Лампочка горит когда не надо", !Selenium.get('bulb').isVisible());
    Selenium.inputString('date-from', '01.01.2002');
    assertTrue("Лампочка не горит когда надо", Selenium.get('bulb').isVisible());
  }
}

По-моему, не очень дорого.

F#
Если фреймворк такой, что это дорого и неудобно, вполне возможно обойтись и юнит тестом.

Честно говоря, я не представляю себе настолько ужасного фреймворка, чтобы юнит-тесты хоть как-то конкурировали по "дёшево и удобно". Впрочем... пример выше, конечно, не является достаточно полным тестированием нашего условного приложения, но представление даёт. Если хотите - набросайте код юнит-тестов, которые дадут сравнимую с этими пятью строками уверенность в его работоспособности :)

F#
Так же интересно знать во скольких еще местах используется этот контроллер.

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

F#
UI тесты для всех них будут зазря гонять одно и то же.

(Пожимая плечами) Вы бьётесь за экономию электричества?

F#
Во-первых, мы будем знать, что контроллер уж точно работает.

Это знание не несёт практической ценности. Когда клиент приходит и говорит "Ваша программа, вашу мать, не работает", слова "ну контроллер-то точно работает" как-то не утешают.

F#
Во-вторых, есть масса идиотских ошибок/описок которые он будет вылавливать и так.

Например?

F#
Можно также сделать тест на viewmodel и focused integration tests на view.

Можно всё. Но прикиньте трудоёмкость и сравните уверенность после. Честно говоря, я почти уверен, что если напишете пример - мы посмотрим в портянку и я назову парочку случаев, которые мой сценарий покрывает, а она - нет.

F#
Если End2End написать достаточно просто, то конечно, можно начать и с него.

В данном случае я не вижу причин писать что-либо кроме него.

F#
Как только мы начнем переиспользовать преобразование строки в дату, эти отдельные тесты начнут окупаться.

Какие "эти"?

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

F#
Для тестирования таких ситуаций, наверное, можно использовать интеграционные тесты например пристегнув временное хранилище.

А смысл? "Пристегнув временное хранилище" - это куча трудоёмкости, а проблему оно всё равно не отловит - потому что временное хранилище работает "как его написали Вы", а не "как работает используемый в приложении компонент".

F#
Хотя по моему опыту такие штуки (связанные с UI в динамике и не отловленные при первом ручном прогоне) скорее находят при exploratory тестировании просто потому, что они обычно находятся вдали от основных сценариев.

Когда бы и как бы их ни находили, от них надо защищаться. Скажем, опытный тестировщик такой тест вставит сразу - потому что уже напарывался и знает, что с этим может быть проблема, да и вообще, что особые случаи всегда надо проверять. Но! Вопрос в том, что этот тест через интерфейс проблему отловит всегда - как только она появится, например, после смены версии библиотеки компонент. "Временное хранилище" эту проблему не отловит вообще никогда, просто потому, что в нём нельзя закодировать "работай как вот такой компонент".

F#
Имхо, если бизнес-логика смоделированна в терминах близких к предметной области, то после разработки бизнес объектов получается достаточно устойчивый набор модулей для которого просто писать тестовые сценарии, причем как при подготовке тестовых предусловий, так и для проверки результатов.

Проблема в том, что эти тесты мало что покрывают. Они проверяют, что бизнес-объект правильно работает, если его правильно вызвать, но они нисколько не гарантируют, что он будет правильно вызван.

F#
Поэтому писать Unit и интеграционные тесты как правило проще.

Чем что? Скажу так: это утверждение представляется мне очень сомнительным. Было бы интересно, если бы Вы привели пример стиля примера с лампочкой - задача, её реализация, тесты к ней, которые "писать проще". В общем, вменяемый по размерам пример, на котором можно было бы сравнить. Можно и на примере лампочки, если считаете его достаточно удобным для демонстрации преимуществ подхода.

F#
Программная модель UI обычно перегружена несущественными для теста подробностями и пользоваться ей напрямую сложнее и усилий требуется больше.

В этой фразе моё изумление вызывает сразу всё. Что такое программная модель UI, какими подробностями она перегружена, зачем ей пользоваться и почему это требует больше усилий.

"Ввёл одну дату - ввёл вторую дату - зажглась лампочка" - это то, как мыслит пользователь, это то, как мыслит тестировщик, это то, как мыслит руководитель проекта, и даже разработчик начинает именно с этой мысли. Тест буквально реализует эту мысль и гарантирует удовлетворение хотелки. Вот хоть убей не вижу здесь "лишних подробностей", которые мешают, и "лишних усилий", от которых хотелось бы избавиться.

F#
В первую очередь я бы хотел, чтобы "код, который рушит" встретился с "тестом, который он обрушит" прежде, чем с пользователем. У юнит-тестов с этим большие проблемы. Когда эта задача решена - можно говорить о желаемой скорости обратной связи.

Я думаю, то тут вопрос в соотношении вероятностей - все не протестируешь.

Именно так, в соотношении. У интерфейсного теста широчайшее возможное покрытие. Если сесть и тщательно записать всё, что он ненароком проверяет - выходят десятки, а то и сотни юнит-тестов и того, что описать юнит-тестами практически невозможно.

Опять же пример из практики: из-за ошибки при установке размера часть формы оказалась недоступна (форма оказалась меньше, чем содержимое этой формы). В результате пользователь просто не мог ввести некоторые поля. Интерфейсный тест тут же встал на уши. Вот скажите - каким "юнит-интеграционным-временным-хранилищем" Вы бы такое отловили? Причём - ключевой момент - не думая об этом заранее, не пытаясь написать теста специально на проверку такой дикой ситуации?

Интерфейсный тест максимально близко воспроизводит действия пользователя и потому натыкается на те проблемы, на которые наткнётся реальный пользователь. В отличие от него, любые тесты, о которых говорите Вы, тестируют не реальное приложение, а модель, и заведомо не могут отловить проблемы, связанные с несоответствием модели реальности. Скажем, тупо: программист написал Application.MainForm.setVisible(false). Всё, приложение полностью неработоспособно. Но ни один Ваш тест этого не обнаружит.

F#
Если тесты гоняются раз в несколько дней, то есть вероятность, что после последнего прогона закоммитили что-то ломающее тесты.

Гоняйте чаще, я только за :)

Если тесты мало что покрывают, то есть вероятность, что закоммитили что-то не ломающее тесты, но и не работающее. Но к этому случаю такого же простого рецепта лечения не припишешь. Поэтому я не вижу причины ставить во главу угла критерий третьестепенной важности. В коммите "чего-то, ломающего тесты" нет ничего страшного. Страшно - пропустить ошибку к пользователю.

F#
К тому же end-to-end тесты неточно показывают место, где произошла ошибка и поиск конкретного место может занять время. Проще не коммитить ошибок чем их потом искать.

Демагогия. Поиск ошибок никак не связан с коммитом. Сделал ошибку - придётся искать. А до коммита или после - вопрос технический.

F#
С другой стороны, если сделать UI очень тонким, то вероятность сделать в нем ошибку сильно уменьшается.

UI нужно делать не толстым и не тонким, а соответствующим задаче. Дизайн интерфейса не должен зависеть от того, что разработчик не умеет что-то хорошо тестировать.

F#
Особенно сделать регрессию. Например после связывания кнопки "Назад" с командой "Назад" маловероятно, что ее кто-то поменяет на "Вперед".

Не скажите, на большой дистанции случается регулярно. Чаще всего - при внедрении или доработке какого-либо стандартного кода, действующего на много мест сразу.

F#
Возможно вам встречалось больше кода, связаного именно с логикой пользовательского интерфейса, чем мне.

Суть не только в логике пользовательского интерфейса. Суть в том, что именно здесь нанизываются все возможные модули, все возможные взаимодействия, короче говоря - все возможные проблемы. Я привожу примеры из логики интерфейса потому, что они очень просты и наглядны сравнительно с другими вариантами.

F#
Обычно по стектрейсу видно место где последствия ошибки обнаружились. Это может быть далеко от места, где находится ошибочный код.

"Средняя длина пути" определяется тем, какую часть кода покрывает тест.

F#
Зависит. Если есть включает в себя много последовательных шагов, то, очевидно, ошибку найти труднее чем если там 1 шаг с подготовленными данными.

Как говорил один мой друг - "если попробовать доказать очевидный факт, он часто становится вовсе не очевидным, а иногда - вовсе и не фактом". В данном случае прежде всего бросаются в глаза "подготовленные данные" - на привычных мне задачах это уже убивает идею, потому что "подготовка данных для каждого из тестов" по трудоёмкости уже многократно превосходит выигрыш от этого теста за всю историю. Становится дешевле тестировать руками. Во-вторых же, если мы рассмотрим два варианта кода:

public class TestMyObj1 {
  @Test public void total() {
    obj.method1();
    assert("method1", ...);
    obj.method2();
    assert("method2", ...);
    obj.method3();
    assert("method3", ...);
  }
}

public class TestMyObj2 {
  @Test public void method1() {
    obj.method1();
    assert("method1", ...);
  }

  @Test public void method2() {
    obj.method2();
    assert("method2", ...);
  }

  @Test public void method3() {
    obj.method3();
    assert("method3", ...);
  }
}


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

F#
Хороший юнит тест не валится, если ошибка произошла в другом юните.

Верно. Поэтому его полезность редко отличается от нулевой.

F#
А так как их можно запускать очень часто

Я не понимаю, зачем Вы так упорно цепляетесь к частоте. Любые тесты можно запускать так часто, как хочется. Другой вопрос, что частые запуски приложения - один из признаков плохого программиста.

F#
Тут мне видится противоречие - либо мы не исключаем нестабильные тесты и не говорим что 100% гарантии не может дать никто, либо таки мы немного беременны.

Наверное, стоит договориться, о какой сценарии вообще речь и с какой целью мы его анализируем. Я задумался о том, какое же противоречие Вы увидели, и предположил, что Вы восприняли формулировку более категорично, чем я имел в виду. Исключая нестабильные тесты, мы ухудшаем покрытие приложения тестами, соответственно, уменьшаем уверенность в нём, но кардинально ничего не меняется. Если бы у нас было идеальное стопроцентное покрытие - да, это разрушало бы идеал и мы бы становились немного беременными. Но поскольку стопроцентное покрытие - скорее идеал, чем практика, то изменение покрытости с, допустим, 98% на 96%... не является зачатием, имхо :)

F#
И еще надо запускать все тесты перед каждым изменением - иначе в промежутке междц запуском мы тоже не можем быть уверены.

Я ничуть не против дополнительной уверенности но не понимаю религиозного к ней стремления. Наверное, я поражу Вас, но в принципе я не считаю ужасным коммитить неработающий и даже не компилирующийся код. Я вижу расклад таким: допустим, я делаю некоторую задачу, которую делать неделю. Я ухожу вечером домой и перед этим я хочу закоммитить свои изменения. Почему? Да хотя бы потому, что мой винт может грохнуться и я потеряю неделю работы. Другой вопрос, что эти мои изменения не должны попадать ни к кому, кроме меня, пока я их не выпущу. Но это уже вопрос к технологии процесса разработки и сборки.

Надеюсь, я пояснил, почему Ваше внимание к коммиту представляется мне удивительным.

F#
В-общем, вашу точку я понимаю, но сам склоняюсь, скорее к тестовой пирамиде - немного End-to-end тестов с основными сценариями, некоторое количество integration и много детальных unit - как описано у Фаулера и Шора.

Cкажу так. Мне было бы интересно попробовать действительно сравнить подходы на каком-нибудь примере вменяемой трудоёмкости. Но не приходит в голову подходящего как по размеру, так и по сложности.
27 авг 15, 14:11    [18076589]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
softwarer
Честно говоря, я не представляю себе настолько ужасного фреймворка, чтобы юнит-тесты хоть как-то конкурировали по "дёшево и удобно".


Я не знаю веб разработки и свойств селениума. Я видел, как тестер делал все руками, потому, что гуи тест для стабильной работы был снабжен задержками на каждом шаге и они складывались так, что общее время было гораздо медленнее даже ручного тестирования, но зато, чтобы не было ложных падений.

Я видел как из-за ошибки очистки базы данных при прогоне ~1000 тестов, тест номер 200 начинал падать из за ошибки в тесте номер 122 и это было очень трудно выяснить и даже воспроизвести и т.д.

В одном, конечно. У нас приложение из одной формы, больше просто негде. Но раз уж хотите идти в эту степь - обратите внимание, что юнит-тесты придётся переделывать при рефакторингах, а приведённый выше тест останется неизменным.


При рефакторингах рефакторятся так же и тесты - да. Вопрос насколько чаще переделывается UI и бизнеслогика, это раз. Во-вторых, насколько грамотно сфоромулированы сами тесты, это два и поддержка инструментов, это три. Например, в приведенном селениум-тесте я вижу, что связывание с формой осуществляется по строчкам - насколько формально контроллируется соответстие описания формы и теста? Есть статическая проверка этого соответствия? При переименовании контрола в IDE переименуется ли описание в тесте?

Если приложение из одной формы, над которым работает один человек этим все можно принебречь, как и быстродействием.

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


По моему опыту обычно очень трудно переделать одновременно тесты и рабочий код, чтобы тесты ложно проходили правильно. Обычно они начинают ложно валиться.

(Пожимая плечами) Вы бьётесь за экономию электричества?


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

Это знание не несёт практической ценности. Когда клиент приходит и говорит "Ваша программа, вашу мать, не работает", слова "ну контроллер-то точно работает" как-то не утешают.


Всегда будет какая-то разница между окружением клиента и вашим и всегда будет какая-то степень неуверенности. Если у вас UI тесты стабильны и требуют мало обслуживания, то хорошо. Можно оставить их, можно пойти дальше по концепции тестовой пирамиды (например, в приведенном примере оставить один UI тест, который будет проверять, что контроллер правильно присоединили к форме, а всякие вырианты оставить на UT)


F#
Во-вторых, есть масса идиотских ошибок/описок которые он будет вылавливать и так.

Например?


Ну всякие мелочи типа неинициализированных переменных в конкретных ветках и прочее.

F#
Можно также сделать тест на viewmodel и focused integration tests на view.

Можно всё. Но прикиньте трудоёмкость и сравните уверенность после. Честно говоря, я почти уверен, что если напишете пример - мы посмотрим в портянку и я назову парочку случаев, которые мой сценарий покрывает, а она - нет.


Давайте я в последующих сообщениях разовью ваш пример.

F#
Как только мы начнем переиспользовать преобразование строки в дату, эти отдельные тесты начнут окупаться.

Какие "эти"?

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


Если не мы пишем это, можно написать тесты, проверяющие наши предположения относительно компонентов это раз. Можно оставить ограниченное количество UI тестов именно на визуальную составляющую. В любом случае, подумать над вариантами преобразования строки в дату проще отдельно, чем вместе с какими-то бизнес сценариями.


"Пристегнув временное хранилище" - это куча трудоёмкости, а проблему оно всё равно не отловит - потому что временное хранилище работает "как его написали Вы", а не "как работает используемый в приложении компонент".


Эта разница вполне может быть несущественной в контексте функционального тестирования. (Скажем в конкретных тестах может быть не важно fault tolerance и изоляция транзакций и in memory база данных вполне себе покажет достаточную точность тестирования при большем быстродействии).

Вопрос в том, что этот тест через интерфейс проблему отловит всегда - как только она появится, например, после смены версии библиотеки компонент. "Временное хранилище" эту проблему не отловит вообще никогда, просто потому, что в нём нельзя закодировать "работай как вот такой компонент".


Можно написать сфокусированные интеграционные тесты, при некотором количестве повторного использования они будут окупаться. Еще раз, я не против UI тестов - я просто считаю допустимым отказаться от них в случае, если по какой-то причине они дороги и нестабильны. Также не по-моему не стоит отказываться от UT

Проблема в том, что эти тесты мало что покрывают. Они проверяют, что бизнес-объект правильно работает, если его правильно вызвать, но они нисколько не гарантируют, что он будет правильно вызван.


Правильность вызова проверяется либо вручную, либо отдельным интеграционным тестом.

Можно и на примере лампочки, если считаете его достаточно удобным для демонстрации преимуществ подхода.


Ок, в последующих сообщениях.

Что такое программная модель UI,


Приведенный вами тест оперирует не с тем, что видит пользователь (картинка на экране, мышка, клавиатура ) а обращается к копмонентам по внутренним именам - эту совокупность, я называю "программная модель UI"

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

"Ввёл одну дату - ввёл вторую дату - зажглась лампочка" - это то, как мыслит пользователь, это то, как мыслит тестировщик, это то, как мыслит руководитель проекта, и даже разработчик начинает именно с этой мысли.


Я думаю, пользователь все таки мыслит терминами своей задачи - он видит бизнес логику сквозь компоненты, и это более устойчивый понятийный базис чем лампочка. Если лампочку заменить чем-то другим или поле ввода для даты заменить календарем, задача и требования к ней останутся тем же самым.

Опять же пример из практики: из-за ошибки при установке размера часть формы оказалась недоступна (форма оказалась меньше, чем содержимое этой формы). В результате пользователь просто не мог ввести некоторые поля. Интерфейсный тест тут же встал на уши. Вот скажите - каким "юнит-интеграционным-временным-хранилищем" Вы бы такое отловили? Причём - ключевой момент - не думая об этом заранее, не пытаясь написать теста специально на проверку такой дикой ситуации?


Очевидно, ошибка в UI не может быть найдена без задействования UI. Ее может найти только Eend2End или интеграционный тест (например, не обязательно использовать полный backend для проверки этого).

Интерфейсный тест максимально близко воспроизводит действия пользователя и потому натыкается на те проблемы, на которые наткнётся реальный пользователь.


С этим я согласен.

Гоняйте чаще, я только за :)


Я использовал вашу оценку продолжительности прогона тестов :)

Если тесты мало что покрывают, то есть вероятность, что закоммитили что-то не ломающее тесты, но и не работающее. Но к этому случаю такого же простого рецепта лечения не припишешь. Поэтому я не вижу причины ставить во главу угла критерий третьестепенной важности. В коммите "чего-то, ломающего тесты" нет ничего страшного. Страшно - пропустить ошибку к пользователю.


Если тестов перед коммитом нет, то есть вероятность закоммитить и ломаюшее тесты и не проходящее. Если у нас есть хорошо покрытая бизнеслогика и модель представления, можно при ручном тестировании сосредоточиться на визуальном представлении и exploratory тестировании.



Демагогия. Поиск ошибок никак не связан с коммитом.


Проще искать иголку не стогу сена а в той щепотке, которая добавлена недавно
- во-первых, потому, что она меньше
- во-вторых, потому, что она сделана недавно и тобой (помните, это вечное "попробуй понять этот код через год")

UI нужно делать не толстым и не тонким, а соответствующим задаче. Дизайн интерфейса не должен зависеть от того, что разработчик не умеет что-то хорошо тестировать.


Я имею ввиду чисто программный дизайн - тонкий слой, относящийся именно к View.

"Средняя длина пути" определяется тем, какую часть кода покрывает тест.


Я тоже про это. У UT такая длина минимальна и связана с логической труктурой приложения.

потому что "подготовка данных для каждого из тестов" по трудоёмкости уже многократно превосходит выигрыш от этого теста за всю историю. Становится дешевле тестировать руками. Во-вторых же, если мы рассмотрим два варианта кода:


Аргументация здесь http://xunitpatterns.com/Principles of Test Automation.html#Verify One Condition per Test

Надеюсь продолжить с примером.
1 сен 15, 13:40    [18095060]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
F
Надеюсь продолжить с примером.


Начну потихонечку.

Понятно, что этот тест написан чтобы продемонстрировать технологию, а не логическое оформление тестов.

По тесту должны быть понятны требования. Так как по этому тесту требования не понятны, я их придумаю.

Мы видим, что здесь лампочка зажигается, когда "дата с" > "дата по". Давайте пусть лампочка горит когда интервал дат неправильный.

Для начала перепишем тест чтобы это требования было очевидно. Еще мне привычнее C#

[TestClass]
public class DateIntervalInputFormTest : AbstractWebTest {
  [TestMethod]
  public void correctnessIndicatorShouldBeVisibleOnlyIfIntervalIsIncorrect() {
    const string correctDateFrom =  "01.01.2000";
    const string correctDateTo = "01.01.2001";
    Selenium.inputString("date-from", correctDateFrom);
    Selenium.inputString("date-to", correctDateTo);
    
    Assert.IsTrue("Лампочка не должна гореть, если интервал корректный", !Selenium.get("bulb").isVisible());
    
    const string dateGreaterThenDateTo = "01.01.2002";
    Selenium.inputString("date-from", dateGreaterThanDateTo);
    Assert.IsTrue("Лампочка должна гореть, когда дата c > дата по", Selenium.get("bulb").isVisible());
  }
}


Итого: требования сталии более понятны из теста.

Продолжение следует...
8 сен 15, 15:48    [18123780]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
F#
Guest
Давайте теперь рассмотрим требования. Я бы их составил в таком виде:

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

Если посмотреть структуру требований, то обычно отделяют проблему от решения.

Так в данном случае на некотором уровне абстракции, пользователю надо ввести интервал и узнать, что он невалидный. Будет для этого использоваться форма, несколько форм, импорт из какого-то формата - это уже конкретный способ ввести интервал.

Мне также удобно, что описание условий валидности и невалидности интервала в требованиях была отделена от способа его ввода (чтобы каждый раз не писать "лампочка должна загораться, если дата с больше чем дата по", "лампочка не должна гореть, если дата с не больше чем дата по").

Итого, проанализировав требования и применив к ним объектную декомпозицию (а так же уточнив все интуитивно понятия типа "интервала дат") получаем:

Есть ИнтервалДат состоящий из ДатыС и ДатыПо. У него есть булевское свойтство ЯвляетсяВалидным, которое должно быть Истиным тогда и только тогда, когда ДатаС <= ДатаПо

Нужна ФормаВводаИнтервалаДат на которой:
1. Есть два элемента управления для ввода дат
2. Лампочка, индикатор алидности

Которые должны отображать соответствующие свойства ИнтервалаДат.

Соответственно тесты:

[EndToEnd] ФормаВводаИнтервалов:
- индикаторВалидностиДолженБытьВключенЕслиИнтервалВалиден
- индикаторВалидностиДолженБытьВыключенЕслиИнтервалНевалиден

[UnitTest] ИнтервалДат:
- интервалДолженБытьВалиденЕслиДатаСНеБольшеДатыПо
- интервалДолженБытьНевалиденЕслиДатаСБольшеДатыПо

При таком наборе тестов есть некоторая избыточность, обычно, она окупается при несколько более усложненных требованиях. Например, если мы добавим новые условия невалидности для интервала (например даты интервала должны принадлежать одному году) мы дорабатываем только UnitTest на интервалы дат, но само по себе отображение остается тем же.

С UT нам легче работать:
- Он работает через программный интерфейс, поэтому:
- нам не надо связываться с элементами управления через и строковые идентификаторы
- нас поддерживает intellisence и компилятор
- нас поддерживают всяческие инструменты для рефакторинга

- Он быстрее

- Модуль можно использовать в других контекстах (как только мы создадим вебсервис, для ввода интервалов, нам не надо будет дублировать тесты - главное создать по одному примеру валидного и невалидного интервала)

- Юнит-тесты так же проверяют, что существует по крайней мере второй (тестовый) контекст использования модуля и помогают нам разделять нашу программу не независимые кусочки. Т.е. некоторой степени способствует пониманию программы по принципу "разделяй и властвуй"

Дальше будет процесс...
15 сен 15, 12:40    [18150727]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
Иммануил Кант
Member [заблокирован]

Откуда:
Сообщений: 3490
egorych
YesSql
Сдесь не сказано когда. Это например может случиться при нажатии на клавишу "исполнить" - сработает бизнес логика и вернет в Гуй ошибку. по которой он (Гуй) может раскрасить поля или просто скажет боксом - Это уже информирование.
почему то вы считаете, что "информирование" - это какой то отдельный зверь. Информирование - это неотъемлемая часть бизнес-процесса, без которой этот бизнес-процесс не имеет смысла. И как оно может быть не бизнес-логикой, скажите мне пожалуйста?
Конкретная реализация мессаджбокса, грида или другого какого чёрта-в-ступе-для-вывода-сообщений-пользователю, конечно, к бизнес-логике отношения не имеют, а вот его реакция на определённое событие - является.

И вот что я не понимаю возвращаясь, всё же, к заявленной теме - как тут применить TDD, когда программируешь такие кейсы. А ведь именно здесь они и нужны больше всего.


1. даешь тестовый вход, заведомо содержащий данные, на которые должна быть предсказуемая реакция
2. проверяшь, была ли реакция
11 мар 16, 19:41    [18921504]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
Иммануил Кант
Member [заблокирован]

Откуда:
Сообщений: 3490
ViPRos
если на граничных точках теория дает сбой, то фигня это а не теория, так как граничные точки самые важные


обычно на них и концентрируется тестирование.
11 мар 16, 19:45    [18921516]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
ioc_ioc
Member [заблокирован]

Откуда:
Сообщений: 44
А как гуру делают тест с обращением к БД. Я сейчас, для себя, принял такое решение:
- такой тест на мок БД (например, когда для одного контекста используется SQL SERVER, или Sql Lite) не сильно интересен
- на БД, с которой работает приложение, для теста, просто не регистрировать АОП [Transaction] для методов, которые пишут в БД. нет коммита - нет косяка
5 авг 16, 19:49    [19507621]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
ioc_ioc
Member [заблокирован]

Откуда:
Сообщений: 44
ioc_ioc
нет косяка


то есть состояние БД не испортил. профит
5 авг 16, 19:51    [19507628]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
scf
Member

Откуда:
Сообщений: 1473
ioc_ioc,

https://github.com/yandex-qatools
https://flywaydb.org/

И, разумеется, роллбэк после каждого теста.
5 авг 16, 20:13    [19507687]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
ioc_ioc
Member [заблокирован]

Откуда:
Сообщений: 44
scf
ioc_ioc,

https://github.com/yandex-qatools
https://flywaydb.org/

И, разумеется, роллбэк после каждого теста.


что-то я от Ваших ссылок ничего не понял. это вообще на что ссылки?
5 авг 16, 20:44    [19507789]     Ответить | Цитировать Сообщить модератору
 Re: Про TDD  [new]
mini.weblab
Member

Откуда:
Сообщений: 632
кстати, тдд можно попробовать на практике, участвуя в онлайн турнирах по программированию
=)
8 янв 17, 15:59    [20082055]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: Ctrl  назад   1 2 3 4 [5] 6   вперед  Ctrl      все
Все форумы / Программирование Ответить