какие функции и / или классы нельзя тестировать и почему

21

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

manizzzz
источник
2
Ваше оправдание? А сотрудники? Менеджеры? На каком языке вы работаете?
1
Много унаследованного кода в приложении и нет времени на редизайн.
Кнут
4
@gnat: я не согласен. Вы задали вопрос о ситуациях, когда юнит-тесты бесполезны. Текущий вопрос касается ситуаций, которые затрудняют юнит-тесты.
Арсений Мурзенко
@MainMa, видимо, мы читаем разные вопросы. «Я пытаюсь понять, какой тип дизайна и кода не может быть модульным тестированием». => "Когда это уместно, чтобы не модульный тест?"
Комнат
1
@manizzzz: вы можете отредактировать это в вопросе.
Jmoreno

Ответы:

27

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

Некоторые примеры кода, которые, вероятно, будет сложно протестировать:

  • Функция 1000-LOC,
  • Код, который сильно зависит от глобального состояния,
  • Код, который требует конкретного, усложняет создание объектов, таких как контекст базы данных, вместо того, чтобы полагаться на интерфейсы и Dependency Injection,
  • Код, который выполняет медленно ,
  • Код спагетти,
  • Устаревший код, который изменялся годами, не заботясь о удобочитаемости или удобстве обслуживания,
  • Трудно понять код, который не имеет комментариев или подсказок об исходном намерении автора (например, код, который использует имена переменных, такие как function pGetDp_U(int i, int i2, string sText).

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

Арсений Мурзенко
источник
8
Также трудно протестировать код, который не вводит зависимости от не чистых функций, таких как случайные числа, текущее время, аппаратный ввод-вывод и т. Д.
9000
тривиально тестировать подобный код - вам просто нужны правильные инструменты тестирования, а не манипулирование вашим кодом в соответствии с ними. Попробуйте Microsoft Fakes для примера.
gbjbaanb
@MainMa, мне нравится этот ответ. Хотели бы вы также прокомментировать, какие факторы подталкивают различные тесты к интеграции и тестированию системы? Я знаю, что причина, по которой я задавал вопросы, подобные тем, что были здесь в прошлом, заключается в том, что у меня не было дорожной карты, объясняющей, какие типы тестов лучше всего ставить, где (или, возможно, наиболее рентабельно, где), - я подумал модульные тесты были единственными в своем роде.
J Trana
14

Есть много вещей, которые делают код сложным для модульного тестирования. По совпадению, многие из них также затрудняют поддержку кода:

  • Нарушение закона Деметры .
  • Создание объектов внутри метода вместо введения зависимостей .
  • Тесная связь.
  • Плохая сплоченность
  • Полностью зависит от побочных эффектов.
  • Полностью полагается на глобалы или синглтоны.
  • Не дает много промежуточных результатов. (Однажды мне пришлось провести модульное тестирование десятистраничной математической функции с одним выходом и без доступных промежуточных результатов. Мои предшественники в основном жестко запрограммировали любой ответ, который дал код).
  • Зависит в большой степени и напрямую от служб, которые трудно подделать, таких как базы данных.
  • Среда выполнения значительно отличается от среды разработки, как встроенная цель.
  • Единицы доступны только в скомпилированном виде (например, сторонние библиотеки DLL).
Карл Билефельдт
источник
Я думаю, что это отличный ответ. Вы затрагиваете многие проблемы уровня кода и проблемы глобального состояния. У @MainMa есть и другие проблемы, которые, на мой взгляд, действительны, но менее четко определены. Джеффри Томас упоминает о I / O и UI. Я думаю, что если вы добавите хорошие части этих трех ответов, у вас получится отличный сплоченный ответ. Мне больше нравится этот ответ, потому что он сосредоточен на антипаттернах кода.
M2tM
1
Ага, ничто не хуже, чем утверждение модульного теста, которое не имеет никакого сходства с бизнес-требованиями и является всего лишь выходом в определенный момент времени - например, макеты, которые настроены так, чтобы их вызывали 3 раза? Почему 3? Потому что это был 3 первый раз, когда тест был запущен / разглагольствован :)
Майкл
Плотное сцепление плохо только тогда, когда оно неуместно. Тесная связь в коде, что он очень сплоченный, является необходимостью. Например, объявление переменной с последующим ее использованием. Плотно связанный, очень сплоченный.
dietbuddha
1
Модульные тесты, в которых выходные данные проверяются на соответствие тому, что сделал код, без какого-либо экономического обоснования / обоснования, называются характеристическими тестами. Они используются в техобслуживании, где ранее не было тестов и часто не было документированных требований, и вам нужно что-то добавить, что сломается, если выходные данные этой функции изменятся. Они лучше, чем ничего.
Энди Кроуел
5

Типичные примеры кода, которые люди не хотят тестировать:

  • Код, который напрямую взаимодействует с вводом / выводом (чтение файлов, прямые сетевые вызовы,…).
  • Код, который напрямую обновляет интерфейс.
  • Код, который напрямую ссылается на синглтоны или глобальные объекты.
  • Код, который неявно изменяет состояние объекта или подобъекта.

Используя макет фреймворка, все эти примеры могут быть проверены модулем. Это просто работа по настройке фиктивных замен для внутренних зависимостей.

Вещи, которые действительно не могут быть проверены модулем:

  • Бесконечные циклы (для менеджера потоков, драйвера или другого типа длинного кода)
  • Некоторые типы операций прямой сборки (которые поддерживаются некоторыми языками)
  • Код, который требует привилегированного доступа (не невозможно, просто не очень хорошая идея)
Джеффри Томас
источник
2

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

Плохо написан разработанный код

  • несоответствующее сцепление (обычно жесткое сцепление там, где его не должно быть)
  • код кухонной раковины (где у функции слишком много логики / обязанностей)

Уверенность государства в другой сфере

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

  • Одиночки
  • Глобалы
  • Затворы

Внешнее / состояние системы

  • Аппаратные / аппаратные зависимости
  • Сетевые зависимости
  • Зависимости файловой системы
  • Межпроцессные зависимости
  • Другие зависимости системных вызовов

совпадение

  • Потоки (блокировки, критические секции и т. Д.)
  • fork'ing
  • Сопрограммы
  • Обратные звонки
  • Обработчики сигналов (не все, но некоторые)
dietbuddha
источник
2

Нет такого понятия, как код, который нельзя протестировать. Однако есть несколько примеров кода, который ДЕЙСТВИТЕЛЬНО, ДЕЙСТВИТЕЛЬНО трудно протестировать (возможно, не стоит затраченных усилий):

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

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

Майкл Кохне
источник
0

Мои основные три группы для этого:

  • код, который опирается на внешние сервисы

  • системы, которые не позволяют тестерам изменять состояние независимо от приложения.

  • тестовые среды, которые не повторяют производственную настройку.

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

Майкл Даррант
источник