несколько вопросов о unit testing
От: TheOldMan  
Дата: 01.08.13 09:55
Оценка:
Здравствуйте!

Есть достаточно большой проект (на python) без unit test'ов. Планируем для новой функциональности, для новых фичь писать unit test'ы. Так как опыта в написании unit test'ов мало, ну и в общем больше знаю теории чем практики, меня очень интересуют best practices, то есть лучшие способы применения, которые бы поспособствовали тому, чтобы unit test'ы действительно стали хорошим инструментом в разработке нашего проекта. Хочу написать свои мысли о unit testing'e, которыё сформировались по ходу изучения этого вопроса... Да и вопросов осталось очень много...

1) в книге "The art of unit testing" прочитал, что 20% покрытие кода тестами — это слишком мало, тоесть плохо. А какой процент можно считать хорошим?
В общем, у меня сформировалось наверное достаточно идеалистическое убеждение в том, что лучший результат достигается, если покрывать 100% кода. Конечно на практике вряд ли это возможно. Хотя бы потому, что нету смысла тестировать простые get/set методы ну и тому подобное. Но всё же мне кажется, что очень важно покрывать как можно больше, иначе unit testing, как инструмент, будет терять свои качества. И вот интересный вопрос. С уменьшением показателя code coverage эффективность unit test'инга падает линейно или экспоненциально? Мне кажется, что экспоненциально. Это верно?

2) верным ли будет то, что даже если процентный показатель покрытия остается не таким и большим, ну, например 50%, — это тоже намного лучше чем ничего? Или все же с таким показателем вернее будет сказать: лучше ничего чем такой невысокий показатель?

3) кроме всего прочего разрабатываем web-приложение. Собираемся покрывать unit test'ами контроллеры. Как вы думаете, могут ли unit test'ы послужить хорошей службой, если тестировать только главные методы (входные точки, public методы) контроллеров? Или все же важно тестировать так же и всё, что находится за кулисами, даже если там тривиальный код? Или всё тривиальное (private методы и тому подобное) можно спокойно пропускать? (простые get/set методы поскольку понимаю можно пропускать, но что если взять, что-то чюточку сложнее?)

4) у меня сформировалось убеждение, что самая лучшая практика написания unit test'ов состоит в том, чтобы писать только один assert в одном unit test методе. Даже если в тестируемом методе последовательно находятся, например, три разных вызова:

foo()
bar()
baz()


, то лучше всего написать три отдельных unit test'а, каждый из которых подтвердит каждый вызов. Верно ли это?

5) Сложилось впечатление, что если писать не по TDD, это плохо сказывается на качестве unit test'ов. Или это совсем не важно пишутся ли unit test'ы по TDD или нет?

6) Насколько я понял, перед тем как командой писать unit test'ы очень важно сформулировать подробный и четкий список соглашений по котором будут писаться unit test'ы. Например, там будут описаны соглашения о именах методов, классов, о том, что нельзя писать больше одного assert'а в рамках одного unit test метода и тому подобное. Наверняка ваша команда пользуется подобными соглашениями, или это не верно? Иначе, если писать unit test'ы без соглашений, то их будет трудно поддерживать разными разработчиками.

7) На какие грабли можно натолкнуться в нашем случае, когда мы только-только приступаем к практике unit test'ов?

8) Какие best practices? Что посоветуете?


Буду очень благодарен за любые заметки, комментарии, пожелания!



Спасибо!
суть в простоте, а простота в сути
Re: несколько вопросов о unit testing
От: ulu http://sm-art.biz
Дата: 01.08.13 10:29
Оценка: 12 (4) +1
Надо гоняться не за количеством, а за качеством. Надо помнить, зачем Вы пишете тесты, и тогда станет ясно, где их можно не писать. Например, если Вы их пишете для улучшения качества продакшн кода (как и предполагается в TDD), то понятно, что геттеры/сеттеры можно пропустить. Опять же, если код, вызывающий этот геттер, покрыт тестом, то и сам геттер покрыт (если только Вы не собираетесь его стабить).

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

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

По поводу одного ассерта -- должен быть один *логический* ассерт. Опять-таки, если тест примерно соответствует пользовательской истории, то все сразу понятно. Например, если у меня регистрация должна заносить пользователя в базу и отсылать ему письмо, то ясно, что должно быть два отдельных теста.

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

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

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

Ну вот, это примерное содержание книжки, которую я когда-нибудь напишу.

Здравствуйте, TheOldMan, Вы писали:

TOM>Здравствуйте!


TOM>Есть достаточно большой проект (на python) без unit test'ов. Планируем для новой функциональности, для новых фичь писать unit test'ы. Так как опыта в написании unit test'ов мало, ну и в общем больше знаю теории чем практики, меня очень интересуют best practices, то есть лучшие способы применения, которые бы поспособствовали тому, чтобы unit test'ы действительно стали хорошим инструментом в разработке нашего проекта. Хочу написать свои мысли о unit testing'e, которыё сформировались по ходу изучения этого вопроса... Да и вопросов осталось очень много...


TOM>1) в книге "The art of unit testing" прочитал, что 20% покрытие кода тестами — это слишком мало, тоесть плохо. А какой процент можно считать хорошим?

TOM>В общем, у меня сформировалось наверное достаточно идеалистическое убеждение в том, что лучший результат достигается, если покрывать 100% кода. Конечно на практике вряд ли это возможно. Хотя бы потому, что нету смысла тестировать простые get/set методы ну и тому подобное. Но всё же мне кажется, что очень важно покрывать как можно больше, иначе unit testing, как инструмент, будет терять свои качества. И вот интересный вопрос. С уменьшением показателя code coverage эффективность unit test'инга падает линейно или экспоненциально? Мне кажется, что экспоненциально. Это верно?

TOM>2) верным ли будет то, что даже если процентный показатель покрытия остается не таким и большим, ну, например 50%, — это тоже намного лучше чем ничего? Или все же с таким показателем вернее будет сказать: лучше ничего чем такой невысокий показатель?


TOM>3) кроме всего прочего разрабатываем web-приложение. Собираемся покрывать unit test'ами контроллеры. Как вы думаете, могут ли unit test'ы послужить хорошей службой, если тестировать только главные методы (входные точки, public методы) контроллеров? Или все же важно тестировать так же и всё, что находится за кулисами, даже если там тривиальный код? Или всё тривиальное (private методы и тому подобное) можно спокойно пропускать? (простые get/set методы поскольку понимаю можно пропускать, но что если взять, что-то чюточку сложнее?)


TOM>4) у меня сформировалось убеждение, что самая лучшая практика написания unit test'ов состоит в том, чтобы писать только один assert в одном unit test методе. Даже если в тестируемом методе последовательно находятся, например, три разных вызова:


TOM>
TOM>foo()
TOM>bar()
TOM>baz()
TOM>


TOM>, то лучше всего написать три отдельных unit test'а, каждый из которых подтвердит каждый вызов. Верно ли это?


TOM>5) Сложилось впечатление, что если писать не по TDD, это плохо сказывается на качестве unit test'ов. Или это совсем не важно пишутся ли unit test'ы по TDD или нет?


TOM>6) Насколько я понял, перед тем как командой писать unit test'ы очень важно сформулировать подробный и четкий список соглашений по котором будут писаться unit test'ы. Например, там будут описаны соглашения о именах методов, классов, о том, что нельзя писать больше одного assert'а в рамках одного unit test метода и тому подобное. Наверняка ваша команда пользуется подобными соглашениями, или это не верно? Иначе, если писать unit test'ы без соглашений, то их будет трудно поддерживать разными разработчиками.


TOM>7) На какие грабли можно натолкнуться в нашем случае, когда мы только-только приступаем к практике unit test'ов?


TOM>8) Какие best practices? Что посоветуете?



TOM>Буду очень благодарен за любые заметки, комментарии, пожелания!




TOM>Спасибо!
Re: несколько вопросов о unit testing
От: vsb Казахстан  
Дата: 01.08.13 10:34
Оценка: 6 (1) +1
Насчет количества тестов, цитата Кента Бека, автора многих популярных книг по юнит-тестированию: http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests

I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards, but that could just be hubris). If I don't typically make a kind of mistake (like setting the wrong variables in a constructor), I don't test for it. I do tend to make sense of test errors, so I'm extra careful when I have logic with complicated conditionals. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.

Different people will have different testing strategies based on this philosophy, but that seems reasonable to me given the immature state of understanding of how tests can best fit into the inner loop of coding. Ten or twenty years from now we'll likely have a more universal theory of which tests to write, which tests not to write, and how to tell the difference. In the meantime, experimentation seems in order.
Re: несколько вопросов о unit testing
От: Anton Batenev Россия https://github.com/abbat
Дата: 01.08.13 13:11
Оценка: 6 (2)
Здравствуйте, TheOldMan, Вы писали:

TOM> 1) в книге "The art of unit testing" прочитал, что 20% покрытие кода тестами — это слишком мало, тоесть плохо. А какой процент можно считать хорошим?


100%

TOM> В общем, у меня сформировалось наверное достаточно идеалистическое убеждение в том, что лучший результат достигается, если покрывать 100% кода. Конечно на практике вряд ли это возможно. Хотя бы потому, что нету смысла тестировать простые get/set методы ну и тому подобное.


get/set методы точно так же должны быть покрыты тестами. Другое дело, что часто они тестируются благодаря использованию в других тестах и не требуют явно выделенного теста (до первой регрессии в инициализации объекта, рефакторинге или ошибочном copy/paste), но и лишними они не будут.

TOM> 2) верным ли будет то, что даже если процентный показатель покрытия остается не таким и большим, ну, например 50%, — это тоже намного лучше чем ничего?


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

TOM> 7) На какие грабли можно натолкнуться в нашем случае, когда мы только-только приступаем к практике unit test'ов?


Тут главное без фанатизма и не бросаться на амбразуру, а то можно расшибить лоб и получить негативный опыт. Плюс тестирование должно работать вместе с другими автоматическими инструментами контроля (code style checker, детектор copy/paste, детектор г-кода, метрики и т.д.).
avalon/1.0.433
Re: несколько вопросов о unit testing
От: maxkar  
Дата: 01.08.13 16:05
Оценка:
Здравствуйте, TheOldMan, Вы писали:


TOM>1) в книге "The art of unit testing" прочитал, что 20% покрытие кода тестами — это слишком мало, тоесть плохо. А какой процент можно считать хорошим?

TOM>В общем, у меня сформировалось наверное достаточно идеалистическое убеждение в том, что лучший результат достигается, если покрывать 100% кода. Конечно на практике вряд ли это возможно. Но всё же мне кажется, что очень важно покрывать как можно больше, иначе unit testing, как инструмент, будет терять свои качества. И вот интересный вопрос. С уменьшением показателя code coverage эффективность unit test'инга падает линейно или экспоненциально? Мне кажется, что экспоненциально. Это верно?

А цель какая? В условиях неограниченного времени 100% покрытие — это хорошо. А вот если учитывать затраты на тесты, то ситуация становится не такой радужной.

Вот что вы считаете эффективностью? Если считать эффективностью (eff) количество багов, обнаруженных тестами, к общему количеству багов, то получится экспонента. Но только вывернутая в другую сторону. Что-то вроде eff = C1 — C2 * exp(C3 * (1 — coverage))), где coverage — это ваше покрытие. Т.е. при небольшом покрытии в нужных местах вы тестами ловите много багов. А вот дальнейшее увеличение покрытия ловит в относительных цифрах очень мало багов. Так что на практике по отношению цена/качество получается разумным покрывать где-то 10-30% от всего кода. Дальнейшие баги дешевле ловить при ручном тестировании.

Кстати, вы покрытие в чем считаете? 100% покрытие кода не гарантирует правильно. По-хорошему, нужно покрывать даже не строки кода, а трассы (последовательность выполнения операторов кода). Допустим, в методе есть два последовательных if'а. Тогда 100% покрытие кода могут дать два вызова метода. А вот 100% покрытие трасс потребует 4 метода. И какие-то баги могут проявляться только на определенной трассе.

TOM>2) верным ли будет то, что даже если процентный показатель покрытия остается не таким и большим, ну, например 50%, — это тоже намного лучше чем ничего? Или все же с таким показателем вернее будет сказать: лучше ничего чем такой невысокий показатель?


Покрытие — не критерий. Важно, какие именно ошибки вы пытаетесь ловить тестами. И как вообще построен процесс разработки. Например, если у вас разработчик никогда не запускает готовое приложение (а только компилирует, прогоняет тесты и отдает на тестирование), большое покрытие будет хорошим. А вот если программист запускает приложение и смотрит на свои изменения, ряд ошибок ловится сразу же. Поэтому очень хорошо, когда покрыты нужные места (те, которые сразу не заметно). А если тестами покрыт, например, сценарий "приложение вообще запускается", это бесполезный тест.

TOM>3) кроме всего прочего разрабатываем web-приложение. Собираемся покрывать unit test'ами контроллеры. Как вы думаете, могут ли unit test'ы послужить хорошей службой, если тестировать только главные методы (входные точки, public методы) контроллеров? Или все же важно тестировать так же и всё, что находится за кулисами, даже если там тривиальный код? Или всё тривиальное (private методы и тому подобное) можно спокойно пропускать? (простые get/set методы поскольку понимаю можно пропускать, но что если взять, что-то чюточку сложнее?)


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


TOM>4) у меня сформировалось убеждение, что самая лучшая практика написания unit test'ов состоит в том, чтобы писать только один assert в одном unit test методе. Даже если в тестируемом методе последовательно находятся, например, три разных вызова:

TOM>, то лучше всего написать три отдельных unit test'а, каждый из которых подтвердит каждый вызов. Верно ли это?

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

Ну и про один assert вопрос спорный. Писать много методов (для "одного ассерта") — это просто лишний код. Еще подготовка данных может быть одинаковая для одинаковых тестов и т.п. В одном тесте может быть одна "более сложная подготовка" и несколько проверок на работу метода.

Из практики — у меня в тестировании лексера было что-то порядка 200 ассертов при прогоне (там не ручная генерация и не число тестов (явных "тестов" там не было), это именно общее число ассертов). Писать 200 методов было бы очень грустно. Называть их — сложно.

TOM>5) Сложилось впечатление, что если писать не по TDD, это плохо сказывается на качестве unit test'ов. Или это совсем не важно пишутся ли unit test'ы по TDD или нет?

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

TOM>6) Насколько я понял, перед тем как командой писать unit test'ы очень важно сформулировать подробный и четкий список соглашений по котором будут писаться unit test'ы. Например, там будут описаны соглашения о именах методов, классов, о том, что нельзя писать больше одного assert'а в рамках одного unit test метода и тому подобное.

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

TOM>7) На какие грабли можно натолкнуться в нашем случае, когда мы только-только приступаем к практике unit test'ов?

1. Отсутствие контрактов у тестируемых методов/классов. Т.е. тесты что-то тестируют, а вот что и почему — не понятно.
2. Тестирование реализации (т.е. что метод написан "именно в такой последовательности функций") а не контракта. Использование библиотек моков является очень хорошим признаком, который может указывать на эту проблему. Могут быть ситуации, когда моки оправданы. Но в большинстве случаев использование моков говорит о том, что проверяется только то, что "написано внутри метода в правильном порядке". Такие тесты поддерживать тяжело (они на любой рефакторинг ломаются!).
3. Тестирование "очевидных" вещеий и при этом отсутствия тестирования "неочевидных" моментов. Грубо говоря, проверяется только "основное поведение" класса. Например, для веб-сайта будет проверяться, что работает логин пользователя ("в общем случае"). Такое поведение тривиально проверяется пользователем при каждом запуске программы (а также командой тестировщиков да и самим программистом при запуске, чтобы посмотреть на свои изменения). А вот какие-нибудь неочевидные вещи (вроде правильной поддержки имен пользователей в корейской локали) в этом тесте не проверяются. Хотя именно это и стоило бы проверить (сам программист будет использовать обычную локаль и в "короткой программе" тестировщиков такой проверки не значится). Т.е. стоит тестировать то, что можно не заметить при обычном "прогоне" программы. Или то, что может привести к нелокальным изменениям (т.е. поменяли в одном месте, а сломалось совсем в другом).

TOM>8) Какие best practices? Что посоветуете?

Use your brain! И без фанатизма . Тут уже приводили цитату Кента Бека. Если в вашей команде проще поймать определенный класс ошибок без тестов, значит, вашей команде эти тесты не нужны. А вот то, где легко ошибиться (как сейчас, так и при дальнейших изменениях) тестировать стоит.
Re[2]: несколько вопросов о unit testing
От: evgeny.e Китай  
Дата: 30.01.14 11:14
Оценка:
как интересно,
это не тот же кент бек который писал книги по экстремальному программированию (там и объяснено что экстремальное значит что все техники должны выполняться на 100% иначе ничего не работает ваще), и, собственно, ТДД, что подразумевает что тесты пишутся вначале, безальтернативно

а сейчас вдруг появилось "добавить тесты по вкусу"

пару лет подождем, может и о вреде тестов начнет писать
Re[3]: несколько вопросов о unit testing
От: akava Беларусь kavaleu.ru
Дата: 31.01.14 10:57
Оценка:
Здравствуйте, evgeny.e, Вы писали:

EE>как интересно,

EE>это не тот же кент бек который писал книги по экстремальному программированию (там и объяснено что экстремальное значит что все техники должны выполняться на 100% иначе ничего не работает ваще), и, собственно, ТДД, что подразумевает что тесты пишутся вначале, безальтернативно

EE>а сейчас вдруг появилось "добавить тесты по вкусу"

EE>пару лет подождем, может и о вреде тестов начнет писать
Это называется развитие. Ничего не стоит на месте. В том числе и взгляды на вещи. Тем более в столь бурно развивающейся отрасли.
Это нормально.

СУВ, akava
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.