Дублированный код - это такой же запах в коде модульного теста, как и в другом коде. Если у вас есть дублированный код в тестах, это затрудняет рефакторинг кода реализации, потому что вам нужно обновить непропорционально большое количество тестов. Тесты должны помочь вам с уверенностью выполнить рефакторинг, а не стать большим бременем, мешающим вам работать с тестируемым кодом.
Если дублирование уже настроено, рассмотрите возможность более широкого использования setUp
метода или предоставления более (или более гибких) методов создания .
Если дублирование заключается в коде, управляющем SUT, спросите себя, почему несколько так называемых «модульных» тестов реализуют одни и те же функции.
Если дублирование есть в утверждениях, возможно, вам понадобятся какие-то настраиваемые утверждения . Например, если в нескольких тестах есть строка утверждений вроде:
assertEqual('Joe', person.getFirstName())
assertEqual('Bloggs', person.getLastName())
assertEqual(23, person.getAge())
Тогда, возможно, вам понадобится единственный assertPersonEqual
метод, чтобы вы могли писать assertPersonEqual(Person('Joe', 'Bloggs', 23), person)
. (Или, возможно, вам просто нужно перегрузить оператор равенства Person
.)
Как вы упомянули, важно, чтобы тестовый код был читабельным. В частности, важно, чтобы цель теста была ясна. Я считаю, что если многие тесты выглядят в основном одинаково (например, три четверти строк одинаковы или практически одинаковы), трудно обнаружить и распознать существенные различия, не внимательно прочитав и не сравнив их. Итак, я считаю, что рефакторинг для удаления дублирования способствует удобочитаемости, потому что каждая строка каждого метода тестирования имеет прямое отношение к цели теста. Это гораздо полезнее для читателя, чем случайное сочетание строк, имеющих прямое отношение к делу, и строк, которые являются просто шаблонными.
Тем не менее, иногда тесты используют сложные ситуации, которые похожи, но все же значительно отличаются, и трудно найти хороший способ уменьшить дублирование. Используйте здравый смысл: если вы чувствуете, что тесты удобочитаемы и проясняют их намерения, и вам удобно, что, возможно, вам нужно обновить больше, чем теоретически минимальное количество тестов при рефакторинге кода, вызванного тестами, тогда примите несовершенство и переместите на что-нибудь более продуктивное. Вы всегда можете вернуться и провести рефакторинг тестов позже, когда придет вдохновение!
Для тестов важнее читаемость. Если тест не прошел, вы хотите, чтобы проблема была очевидна. Разработчику не нужно продираться через большое количество тщательно продуманного тестового кода, чтобы точно определить, что именно не удалось. Вы же не хотите, чтобы ваш тестовый код стал настолько сложным, что вам нужно было писать модульные тесты.
Однако устранение дублирования - это обычно хорошо, если оно ничего не скрывает, а устранение дублирования в ваших тестах может привести к лучшему API. Просто убедитесь, что вы не преодолели точку убывающей отдачи.
источник
Код реализации и тесты - это разные животные, и правила факторинга к ним применяются по-разному.
Дублированный код или структура всегда являются запахом кода реализации. Когда вы начинаете внедрять шаблонный код, вам необходимо пересмотреть свои абстракции.
С другой стороны, код тестирования должен поддерживать уровень дублирования. Дублирование тестового кода позволяет достичь двух целей:
Я стараюсь игнорировать тривиальное дублирование в тестовом коде, если каждый тестовый метод остается короче примерно 20 строк. Мне нравится, когда в методах тестирования проявляется ритм «настройка-запуск-проверка».
Когда дублирование закрадывается в тестовую часть «проверки», часто бывает полезно определить собственные методы утверждения. Конечно, эти методы должны по-прежнему проверять четко идентифицированную связь, которая может быть очевидна в имени метода:
assertPegFitsInHole
-> хорошо,assertPegIsGood
-> плохо.Когда методы тестирования становятся длинными и повторяющимися, я иногда нахожу полезным определить тестовые шаблоны с заполнением пробелов, которые принимают несколько параметров. Затем фактические методы тестирования сводятся к вызову шаблонного метода с соответствующими параметрами.
По многим вещам в программировании и тестировании однозначного ответа нет. Вам нужно развить вкус, и лучший способ сделать это - ошибаться.
источник
Я согласен. Компромисс существует, но он отличается в разных местах.
Я с большей вероятностью реорганизую дублированный код для настройки состояния. Но менее вероятно, что придется провести рефакторинг той части теста, которая фактически проверяет код. Тем не менее, если для выполнения кода всегда требуется несколько строк кода, я могу подумать, что это запах и рефакторинг реального тестируемого кода. И это улучшит читаемость и ремонтопригодность как кода, так и тестов.
источник
Вы можете уменьшить количество повторений, используя несколько различных видов служебных методов тестирования .
Я более терпимо отношусь к повторению в тестовом коде, чем в производственном, но иногда меня это разочаровывает. Когда вы меняете дизайн класса и вам приходится возвращаться и настраивать 10 различных методов тестирования, которые выполняют одни и те же шаги настройки, это разочаровывает.
источник
Джей Филдс придумал фразу, что «DSL должны быть DAMP, а не DRY», где DAMP означает описательные и содержательные фразы . Думаю, то же самое можно сказать и о тестах. Очевидно, что слишком много дублирования - это плохо. Но удалить дублирование любой ценой еще хуже. Тесты должны действовать как спецификации, раскрывающие намерения. Если, например, вы указываете один и тот же объект с разных точек зрения, следует ожидать некоторого дублирования.
источник
Я ЛЮБЛЮ rspec из-за этого:
У него есть 2 вещи, которые могут помочь -
общие группы примеров для тестирования общего поведения.
вы можете определить набор тестов, а затем «включить» его в свои настоящие тесты.
вложенные контексты.
по сути, вы можете иметь методы «настройки» и «разборки» для определенного подмножества тестов, а не только для каждого в классе.
Чем раньше .NET / Java / другие тестовые среды примут эти методы, тем лучше (или вы могли бы использовать IronRuby или JRuby для написания своих тестов, что, по моему мнению, является лучшим вариантом)
источник
Я считаю, что тестовый код требует того же уровня инженерии, который обычно применяется к производственному коду. Конечно, можно привести аргументы в пользу удобочитаемости, и я согласен, что это важно.
Однако по своему опыту я считаю, что хорошо продуманные тесты легче читать и понимать. Если есть 5 тестов, каждый из которых выглядит одинаково, за исключением одной измененной переменной и утверждения в конце, может быть очень сложно найти, что это за отдельный элемент. Точно так же, если он разложен так, что видна только изменяющаяся переменная и утверждение, тогда легко сразу понять, что делает тест.
Поиск подходящего уровня абстракции при тестировании может быть трудным, и я считаю, что это того стоит.
источник
Я не думаю, что существует связь между более дублированным и читаемым кодом. Я думаю, что ваш тестовый код должен быть не хуже других. Неповторяющийся код более читабелен, чем дублированный код, если он выполнен правильно.
источник
В идеале модульные тесты не должны сильно меняться после того, как они написаны, поэтому я бы предпочел удобочитаемость.
Максимально дискретные модульные тесты помогают сосредоточить тесты на конкретной функциональности, на которую они нацелены.
С учетом сказанного, я стараюсь повторно использовать определенные фрагменты кода, которые я использую снова и снова, например, установочный код, который абсолютно одинаков во всех наборах тестов.
источник
"переработал их, чтобы сделать их более СУХИМИ - цель каждого теста больше не была ясна"
Похоже, у вас возникли проблемы с рефакторингом. Я просто догадываюсь, но если он оказался менее ясным, не означает ли это, что вам еще нужно поработать, чтобы у вас были достаточно элегантные тесты, которые совершенно ясны?
Вот почему тесты являются подклассом UnitTest - поэтому вы можете создавать хорошие наборы тестов, которые будут правильными, легко проверяемыми и понятными.
Раньше у нас были инструменты для тестирования, которые использовали разные языки программирования. Было сложно (или невозможно) создать приятные, простые в работе тесты.
У вас есть все возможности - независимо от того, какой язык вы используете - Python, Java, C # - так что используйте этот язык как следует. Вы можете получить красивый тестовый код, понятный и не слишком избыточный. Нет компромисса.
источник