Надо гоняться не за количеством, а за качеством. Надо помнить, зачем Вы пишете тесты, и тогда станет ясно, где их можно не писать. Например, если Вы их пишете для улучшения качества продакшн кода (как и предполагается в 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>Спасибо!