«Нормальное» отношение кодовой базы к тестовым утверждениям при неудачном тестировании? - PullRequest
10 голосов
/ 16 февраля 2010

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

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

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

  1. мы создаем списки тестов, которые находятся либо в «зависимом», либо «независимом» сегменте . независимые тесты не требуют ничего, что не входит в систему контроля версий. Таким образом, любые вызовы нашего уровня доступа к данным являются либо поддельными, либо получают данные из XML-файла вместо реального БД, например. Зависимые тесты, как следует из названия, зависят от чего-то вроде файла конфигурации, базы данных или сетевых данных, которые могут быть неправильно сконфигурированы / недоступны при запуске теста. Разделение тестов на группы, подобные этой, было чрезвычайно ценно, поскольку позволило нам написать зависимые «отбрасываемые» тесты для ранней разработки и независимые критически важные тесты, на которые можно положиться, и не поддаваться тестированию. Это также облегчает управление CI-сервером, поскольку его не нужно настраивать и обслуживать соединениями w / db и т.п.
  2. Мы нацелены на разные уровни нашего кода . Например, у нас есть тесты, работающие на main, и тесты на все методы, которые вызовет main. Это дает нам возможность ориентироваться в деталях системы и общих целях. «Основные» тесты трудно отлаживать, если они ломаются, но обычно они не единственные, которые ломаются (детальные тесты также ломаются). Проще следить за подробными тестами и отлаживать их, если они ломаются, но их недостаточно, чтобы знать, убивает ли система рефакторинг (для этого и нужны «основные» тесты).
  3. «Основные» тесты были критически важны для того, чтобы чувствовать себя комфортно, потому что рефактор не скрывал кодовую базу. поэтому «главный» тест будет похож на многие тесты для одного метода, вызываемого с разными аргументами, которые сопоставляются с вариантами использования. По сути, это точка входа в наш код на самом высоком уровне, и, возможно, это не совсем «модульные» тесты. Тем не менее, я обнаружил, что мне действительно нужны тесты более высокого уровня, чтобы чувствовать себя комфортно, чтобы рефактор не взорвал кодовую базу . Тесты более низкого уровня (те, которые действительно являются «единицей» работы) недостаточны.

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

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

РЕДАКТИРОВАТЬ Обратите внимание, что этот вопрос касается изучения того, что означает это соотношение, и вашего опыта работы с этим соотношением. Когда это "вонючий" ??

Ответы [ 4 ]

4 голосов
/ 16 февраля 2010

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

Правильно. Ваши требования изменились. Ваши тестовые утверждения должны измениться.

"это заставляет меня чувствовать, что я просто потратил впустую х часов моего дня"

Почему? Как еще вы собираетесь отслеживать изменения требований?

«Устранение ошибок тест-утверждения часто занимает больше времени, чем фактические ошибки регрессии»

Без шуток. Когда ваши требования находятся в состоянии постоянного изменения, требуется много времени и усилий, чтобы отобразить изменения требований в соответствии с изменениями результатов испытаний.

"что ... противоинтуитивно". Зависит от вашей интуиции. Моя интуиция (после 18 месяцев TDD) заключается в том, что изменение требований ведет к изменениям в дизайне, множеству сложных тестовых изменений, отражающих изменения в дизайне.

Иногда очень мало (или нет) изменений кода.

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

Иди домой счастливым.

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

Если вы потратили час на написание тестов и 4 часа, пытаясь получить код для прохождения тестов, вы либо получили действительно сложный алгоритм (и должен был разбить его на более тестируемые части), либо вы ужасное приложение программист.

Если вы потратили 1 час на написание тестов и 1 час на получение кода, то проходите тесты - это хорошо.

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

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

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

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

Если тест не пройден, есть три варианта:

  1. реализация нарушена и должна быть исправлена,
  2. тест не пройден и должен быть исправлен, или
  3. тест больше не нужен (из-за изменившихся требований) и должен быть удален.

Важно правильно определить, какой из этих трех вариантов это. Как я пишу свои тесты, я документирую во имя теста поведение, которое тест задает / тестирует, так что когда тест не пройден, я легко выясню , почему тест был изначально написан. Я написал больше об этом здесь: http://blog.orfjackal.net/2010/02/three-styles-of-naming-tests.html

В вашем случае

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

(Интересная сторона в том, что если вы в основном переписываете классы, а не меняете их, когда меняются требования, то это может указывать на то, что код хорошо следует SRP , OCP и другие принципы проектирования.)

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

Я определенно второй ответ С.Лотта. Я просто хотел бы отметить, что, когда спецификация устанавливается на кучах мертвых деревьев, происходит следующее: когда изменяются требования, мертвые деревья (или файлы текстового процессора) не кричат ​​на вас так, как это делают тесты, поэтому все работает просто хорошо, за исключением того, что у вас есть эта куча мертвых деревьев, на которые все смотрят и говорят: «документация выдумка».

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

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

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

S. Лотт почти все сказал. Я думаю, что единственное, что соотношение между изменениями тестовых утверждений (T) и регрессионными исправлениями (R) скажет вам, является комбинацией того, насколько изменчивы ваши требования (которые сделают T выше) по сравнению с тем, насколько успешным является код приложения при прохождении кода. тест (который повлияет на значение R). Эти два фактора могут варьироваться независимо в зависимости от качества ваших требований и процессов разработки.

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