Поддерживаемые юнит-тесты - PullRequest
25 голосов
/ 11 февраля 2010

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

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

Так есть ли какие-нибудь хиты / советы для написания поддерживаемого кода TDD? В настоящее время я читаю Роя Ошерова «Искусство модульного тестирования» , есть ли другие ресурсы, которые могут мне помочь?

Спасибо

Ответы [ 11 ]

17 голосов
/ 11 февраля 2010

Практика

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

Рекомендуемая книга xUnit Test Patterns уже хороша, и я слышал хорошие отзывы о книге, которую вы сейчас читаете.

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

Некоторые советы, которым я живу, следуют правилу AAA .

Упорядочить, действовать и утверждать. Каждый тест должен следовать этой формуле. Это делает тест читаемым, и его легко поддерживать, если и когда он ломается.

Дизайн все еще важен

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

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

Ознакомьтесь с Блогом тестирования Google - это был поворотный момент для меня при запуске TDD. Статьи Миско (особенно сайт Guide to Testable code) превосходны и должны указывать вам правильное направление.

7 голосов
/ 11 февраля 2010

"как только требования начали меняться, и я начал проводить рефакторинг, я обнаружил, что потратил больше времени на переписывание / исправление юнит-тестов"

Так? Как это проблема?

Ваши требования изменены. Это означает, что ваш дизайн должен был измениться. Это означает, что ваши тесты должны были измениться.

«Я потратил больше времени на переписывание / исправление модульных тестов, чем на написание кода, фактически больше времени».

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

Так оно и должно работать.

Иди домой счастливым. Вы сделали работу правильно.

5 голосов
/ 12 февраля 2010

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

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

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

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

3 голосов
/ 11 февраля 2010

Похоже, ваши юнит-тесты хрупки и частично совпадают. Единственное изменение кода, в идеале, должно повлиять только на один модульный тест - при сопоставлении тестов один к одному с функциями, другие тесты не зависят от данной функции. Это может быть слишком идеалистичным; на практике многие наши тесты повторяют один и тот же код, но об этом следует помнить. Когда одно изменение кода влияет на многие тесты, это запах. Кроме того, в отношении вашего конкретного примера переименования: найдите инструмент, который автоматизирует эти рефакторинги для вас. Я считаю, что Resharper и CodeRush поддерживают такие автоматические рефакторинги; это гораздо более быстрый, простой и надежный способ рефакторинга, чем ручной подход.

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

2 голосов
/ 11 февраля 2010

Я думаю, вы, возможно, захотите найти достойный баланс между тестированием и кодированием.

Когда я начинаю проект, поскольку требования и цели постоянно меняются, я почти не пишу никаких тестов, потому что, как вы заметили, потребуется много времени, чтобы постоянно их фиксировать. Некоторое время я просто пишу в комментарии «это должно быть проверено», чтобы не забыть проверить его.

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

Когда я начинаю много заниматься рефакторингом, я не слишком беспокоюсь о тестах, пока проект не остановится снова. Я также положил некоторые комментарии "проверить это". Когда рефакторинг закончен, пора переписать все провальные тесты (и, возможно, отбросить некоторые из них, и, конечно, написать несколько новых).

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

2 голосов
/ 11 февраля 2010

Вы широко используете интерфейсы, Dependancy Injection и mocking?

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

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

2 голосов
/ 11 февраля 2010

Да, есть целая книга под названием xUnit Test Patterns , в которой рассматривается эта проблема.

Это книга подписей Мартина Фаулера, так что в ней есть все атрибуты классической книги образцов. Нравится вам это или нет, это вопрос личного вкуса, но я, например, нашел его бесценным.

В любом случае, суть в том, что вы должны обращаться со своим тестовым кодом так же, как со своим рабочим кодом. Прежде всего, вы должны придерживаться принципа DRY , потому что это упрощает рефакторинг вашего API.

1 голос
/ 02 марта 2011

Я нашел полуавтоматический инструмент тестирования разработчика для Java с именем SpryTest . Он предоставляет простой, но мощный пользовательский интерфейс для создания рандомизированных данных. Он также поддерживает насмешливые звонки, используя powermock и easymock. Он может генерировать стандартные тесты JUnit, наиболее близкие к написанным вручную тестам. У него есть тест-> функция синхронизации исходного кода.

Попробовал и работал довольно хорошо для меня. Проверьте инструмент на http://www.sprystone.com

1 голос
/ 12 февраля 2010

Прежде всего, Рефакторинг не нарушает юнит-тесты. Вы применили рефакторинг к книге ? Возможно, ваши тесты тестируют реализацию, а не поведение, что может объяснить, почему они ломаются.

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

0 голосов
/ 15 февраля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...