Является ли дублированный код более приемлемым в модульных тестах?

StackOverflow https://stackoverflow.com/questions/129693

Вопрос

Некоторое время назад я испортил несколько модульных тестов, когда проходил их рефакторинг, чтобы сделать их более удобными. СУХОЙ— цель каждого теста больше не была ясна.Кажется, существует компромисс между читабельностью тестов и удобством сопровождения.Если я оставлю дублированный код в модульных тестах, они станут более читабельными, но если я изменю СУТ, мне придется отслеживать и изменять каждую копию дублированного кода.

Согласны ли вы с тем, что такой компромисс существует?Если да, то предпочитаете ли вы, чтобы ваши тесты были читаемыми или поддерживаемыми?

Это было полезно?

Решение

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

Если дублирование находится в настройке приспособления, рассмотрите возможность более широкого использования 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 должны быть ВЛАЖНЫМИ, а не СУХИМИ», где ВЛАЖНЫЙ означает описательные и содержательные фразы.Я думаю, то же самое относится и к тестам.Очевидно, что слишком большое дублирование – это плохо.Но еще хуже устранить дублирование любой ценой.Тесты должны действовать как спецификации, раскрывающие намерения.Если, например, вы указываете одну и ту же функцию с нескольких разных точек зрения, то следует ожидать определенного количества дублирования.

Я ОБОЖАЮ rspec из-за этого:

Ему помогут 2 вещи:

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

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

Чем раньше .NET/Java/другие среды тестирования примут эти методы, тем лучше (или вы можете использовать IronRuby или JRuby для написания своих тестов, что лично я считаю лучшим вариантом).

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

В идеале модульные тесты не должны сильно меняться после их написания, поэтому я бы склонялся к читабельности.

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

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

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

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

Найти правильный уровень абстракции при тестировании может быть сложно, и я считаю, что это того стоит.

«переработал их, чтобы сделать более СУХИМИ — цель каждого теста больше не ясна»

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

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

Раньше у нас были инструменты тестирования, которые использовали разные языки программирования.Было сложно (или невозможно) разработать приятные и удобные в работе тесты.

У вас есть все возможности — какой бы язык вы ни использовали — Python, Java, C# — так что используйте этот язык правильно.Вы можете создать красивый тестовый код, понятный и не слишком избыточный.Никакого компромисса.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top