Почему лучше всего unit test наименьшая возможная единица кода? Я считаю, что эти тесты никогда не переживут рефакторинг

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

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

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

У кого-нибудь есть похожие или противоположные переживания?

Ответ 1

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

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

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

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

Ответ 2

Это проблема детализации, как Златовласки и три медведя. Вы хотите что-то не слишком маленькое, не слишком большое, но просто.

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

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

Ответ 3

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

Ответ 4

В большинстве случаев я только unit test общедоступные классы и методы. Потому что я думаю, что, как вы сказали, частные члены слишком изменчивы и подвержены изменениям.

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

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

Ответ 5

Звучит не так, как будто вы делаете true Test-Driven Development, для которого требуется итеративный цикл написания теста для небольшого кусок функциональности, создавая функциональность, удовлетворяющую тесту, а затем рефакторинг, чтобы удалить любое дублирование, которое может добавить тест/код. Похоже, что вы тестируете после этого факта ( "код всегда изменяется достаточно значительно, чтобы небольшие модульные тесты просто отбрасывались" ). Если тест является спецификацией функциональности (как и в TDD), рефакторинг никогда не заставит тест "не выживать".

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

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

Я рекомендую вам следовать практике TDD, как описано Kent Beck. Написание тестов после того, как факт лучше, чем никаких тестов, но я считаю, что это гораздо менее производительная практика, чем TDD.

Ответ 6

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