Re[14]: Нафига нужны юнит-тесты?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 06.10.11 14:51
Оценка:
Здравствуйте, gandjustas, Вы писали:

N>>>>Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки.

G>>>Точно, поэтому и нужны unit-тесты чтобы проверить что новый и старый алгоритм эквивалентны ;)
N>>Против этого я и не возражал.:) Вопрос в том, насколько они unit.
G>А что им мешает быть unit-тестами?

Зависит от глубины переделки. Может оказаться, что неизменна и покрыто неизменившимися тестами только самая внешняя функциональность, которая не может быть сведена к простому "выстрелил вызовом функции — получил ответ — проверил", а требует долгой подготовки среды. Для меня это всё-таки не unit тест. Unit — это когда максимум подготовки среды это расстановка заглушек вместо рабочего кода. Да, это деление не академично, но мне оно полезнее.

N>>>>>>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%.

G>>>>>Почему это? Если покрываются все варианты использования, то чем это не доказательство?
N>>>>Что ты называешь вариантами использования?
G>>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения.
N>>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно.
G>Вообще-то конечен.

Ну числа типа 2**2**32 всё равно за пределами представления даже в BER:), поэтому я позволил себе "округлить" их до бесконечности.

G> Но все равно я не предлагаю весь код покрывать, я предлагаю покрывать ту функциональность конракты которой будут меняться редко или не меняться вообще. И как ты понимаешь на fixed cost проектах (каковых большинство) надо заранее планировать объем работ. Из этого объема легко вычленить части, соотвествующие тому что я написал выше.


Я ни разу не планировал сам fixed-cost проекты, поэтому тут ничего не скажу. Приму просто как должное.

G>>>Не надо проверять все параметры. надо написать по одному тесту для каждого варианта использования. Их всегда конечное количество так как путей исполнения кода конечное количество. Как будешь вычислять эти самые варианты — твое дело, хоть автоматически с помощью pex, хоть анализируя код — без разницы. Вообще идеальный вариант — заранее продумывать как будет работать тот или иной метод класса и писать тесты. В любом случае вариантов использование будет очень ограниченное количество.


N>>Под такое ещё надо адаптировать код. Далеко не всегда возможно, хотя, надо заметить, может быть полезно. Для того же примера с подсчётом длины строки, например, разделить код на функцию сдвига на один пункт и код подсчёта пунктов может быть полезно для ловли, например, ситуации, если промежуточная длина хранится в 8 битах (злобный пример того, как можно не заметить фигню на коротких тестах). Но это уже придётся mock'и делать.


G>Если писать тесты до кода, то он как-то сам адаптируется ;)


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

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

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

G>Для TDD (test-first) это естественный процесс, для test-after — нет. Поэтому unit-тесты и рекомендуется для test-first.

Не так. Оцениваешь функциональность. Пишешь прототип, или даже просто схему на бумаге, что должно делаться и как. Смотришь на него, проводишь фактически design review (хоть в одиночку, но можно и с коллегами), оцениваешь, что и как реализовывать. На этом этапе могут выделяться чёткие подблоки, которые пригодны к тестированию отдельно от остального. Но ещё не под тест, потому что могут интерфейсы и контракты поменяться уже в процессе реализации, когда будет осознан ещё один пласт специфики. Потом всё это приблизительно написано, разделение на составные части, интерфейсы, контракты стабилизировались, теперь нетривиальные внутренние части покрываются базовыми тестами, подтверждающими их работоспособность. В основном это юнит-тесты, с минимальной настройкой среды проверки или вообще без неё, на уровне отдельных функций. Далее рассчитываешь и делаешь функциональные тесты, под базовые юзкейсы компонент, вычищаешь их от багов. В моём случае обычно минимальная единица, обладающая собственным поведением, называется приложением, и делать функциональные тесты для более мелких частей обычно нереально (хотя бывают и такие сущности). На следующем уровне тестируются связки из таких приложений, образующие функциональность части полной системы, например, тракт вокруг конкретной шины от первичных генераторов событий до финальных накопителей; назовём это интеграционными тестами. Только после этого можно говорить о завершении разработки конкретной подсистемы или даже приложения — когда оно показывает корректное прохождение интеграционных тестов. Далее идут общесистемные тесты, это уже делается людьми в QA отделе. Процесс такого развития никак не соответствует продвижению сверху вниз с твёрдой уверенностью в функционале конкретного уровня, а вместо этого можно говорить только о приблизительной чёткости понимания, которая будет доведена до полной уже после пробы на прототипе. Вот-с, где-то так.

N>>А без адаптации, по крайней мере для моей обстановки, никак не получается "очень ограниченное количество" ситуаций. Поэтому для меня whitebox testing имеет совсем другой смысл (см. предыдущие письма).

G>То есть изначально написан плохо тестируемый код. Но эту ситуацию ты экстраполируешь на другие случаи, что далеко неверно.

Я продолжу эксплуатировать тот же пример — подсчёт codepoints. Ты действительно считаешь, что если функцию такого рода нельзя разделить на части, проверяемые по методу такого whitebox и подменяемые mock'ами, то это "изначально плохо тестируемый код"? Ответь, пожалуйста, конкретно, а не в сторону. Мне таки очень интересно. Пример простой, но полезный.

N>>Ты путаешь совершенно разные вещи. Ошибки часто одинаковые, это да. Специфика не уникальна, но она колоссально разная. Я не могу найти сейчас человека здесь на форуме, который заметен и который занимается хотя бы примерно теми же задачами, что я. Если есть, то они не светятся. В то же время я знаю, что в мире ещё около 5 групп идёт схожими путями. Но они не здесь. Поэтому я действительно склонен считать, что в пределах RSDN мои задачи уникальны и специфика их — тоже. Возражать разрешаю только на собственных примерах;)

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

Никакой завесы. Текущая основная задача — система управления и мониторинга HPC кластеров. Основной код на Erlang, всякий клей на Python. Предыдущая — VoIP свич, в основном Python, местами C. Ещё на одну до того — вообще не программизм по сути, хотя много всякой ерунды на Perl и C для автоматизации работы и вокруг. Дальше в историю копать не буду, а то и до матфизики на Фортране докопаемся.

Ожидаю теперь такой же откровенности с твоей стороны:)

G>Посмотрев пару сотен сообщений в твоем профиле видно что большую часть ты тут пишешь в КСВ и о жизни, инода отвечая на темы о сетевых протоколах и С++.


C++? Вот именно в него я не лезу, по крайней мере вглубь.

G>Поэтому то чем ты занимаешься связано с сетями, точно не web, скорее всего linux\C++. Возможно что-то в высокой вычислительной нагрузкой типа аудио\видео конференц связи или обработкой видеопотоков от камер.


Linux — да. Ещё FreeBSD. C++ — нет. VoIP — было, но не сейчас. Вычислительная нагрузка — да. Конференц-связь — нет, бог миловал. Видеопотоки — тоже нет, не путай меня с Лапшиным;)

G>Учитывая что основной язык для тебя — C++,


Ну вот объясни мне — как можно было _так_ читать, чтобы прийти к такому выводу???

G> то там unit-тесты с трудом возможны, ибо компиляция долгая и нормальных mock-фреймворков нету.


А если не заниматься безумными гипотезами и учесть, что у меня Erlang и проблем с юнит-тестами в общем-то нет?
Mock'и подставить тривиально, это условия запуска в рантайме. Адекватная модульность обычно или заложена, или легко закладывается.
The God is real, unless declared integer.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.