Нужен ли мне модульный тест, если у меня уже есть интеграционный тест?

47

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

Что я знаю преимущество модульного теста над интеграционным тестом

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

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

Брайан Чен
источник
Просто из любопытства, какой охват ваши интеграционные тесты? Кроме того, если у вас есть какая-то сложная логика, охватывают ли ваши интеграционные тесты их все или только часть, и какие части важны для бизнеса или случайны?
civan
Этот вопрос кажется неоднозначным. Допустим, у меня есть метод контроллера Rails, который вызывает интерактор ( github.com/collectiveidea/interactor ). Я решил написать интеграционные тесты для моего метода контроллера (потому что без них я не могу поверить, что моя конечная точка API работает). Здесь есть по крайней мере два вопроса: (1) я должен также написать модульные тесты для моих методов контроллера (то есть, я должен написать модульные тесты, которые проверяют то же поведение, что и мои интеграционные тесты), и (2) я также должен написать модульные тесты для моего интерактора? Я бы ответил на эти два вопроса по-разному в определенных бизнес-контекстах.
Даниил

Ответы:

33

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

  • Маленький и быстрый - это хороший аспект модульного тестирования, хотя и не самый важный.
  • Locating-bug [s] -easier чрезвычайно ценен. Многие исследования в области профессиональной разработки программного обеспечения показали, что стоимость ошибки резко возрастает по мере ее старения и продвижения по конвейеру доставки программного обеспечения.
  • Поиск замаскированных ошибок ценен. Когда вы знаете, что для определенного компонента проверено все его поведение, вы можете с уверенностью использовать его ранее. Если единственной проверкой является интеграционное тестирование, вы знаете только то, что его текущее использование ведет себя правильно.
  • В реальных случаях издевательство обходится дорого, а поддержание насмешек - вдвойне. На самом деле, при имитации «интересных» объектов или интерфейсов вам могут даже понадобиться тесты, которые проверяют, что ваши фиктивные объекты правильно моделируют ваши реальные объекты!

В моей книге плюсы перевешивают минусы.

Росс Паттерсон
источник
2
Разве издевательство не дешевле в реальных случаях? Вы только устанавливаете ожидания того, какие сообщения получает макет, и указываете, что он вернет.
Dogweather
10
@Dogweather Mocks хорошо работают при первом создании. С течением времени и реальный объект меняется, издевательства должны меняться вместе с ним. 10 лет и 6000 юнит-тестов в будущем, очень трудно понять, что ваши «успешные» тесты действительно успешны.
Росс Паттерсон
1
Кроме того, «10 лет и 6000 единичных тестов» - это цифры из личного опыта, а не гипербола.
Росс Паттерсон
3
Я начал писать автоматизированные тесты для разработчиков (модульные и интеграционные, но в основном последние) еще в 2004 или 2005 году и разработал расширенную библиотеку-макет для Java. Много раз я видел, как несколько интеграционных тестов ломались по одной и той же причине (например, изменение в БД или изменение сети), и я иногда писал «интеграционные тесты более низкого уровня» для повторно используемых компонентов, но все же я предпочитаю иметь только интеграционные тесты. Изолированные модульные тесты с насмешками, в большинстве случаев, просто не стоят того; Я применяю насмешки только в качестве помощи для интеграционных тестов.
Рожерио
Ваше последнее замечание кажется противоречащим аргументу, который вы выдвигаете. В этом пуле вы приводите доводы против написания ненужных макетов, что является аргументом против написания модульных тестов для частей, которые уже охвачены интеграционными тестами. Однако ваш аргумент в пользу написания модульных тестов для частей, которые уже охвачены интеграционными тестами. Не могли бы вы уточнить свои намерения здесь @RossPatterson?
Даниил
8

Я не вижу особой ценности в переосмыслении существующего интеграционного теста в качестве юнит-теста.

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

> Then what are the reasons to write/add unit tests?

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

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

Если вы делаете TDD, код автоматически легко тестируется.

k3b
источник
2
Если у меня есть устаревшее приложение без unit-tests, и я хочу включить unit-testsдля определенных частей, как вы думаете, лучше сначала написать integration testsдля устаревшего кода. Как только это написано, тогда вы можете отсоединить тесную связь и создать функции с интерфейсами, которые можно протестировать? Поскольку вы integration-testуже написали, вы можете проверять на каждом этапе рефакторинга, что новая версия кода по-прежнему работает как нужно?
alpha_989
2
@ alpha_989, это очень много, ИМХО, и я готов услышать другие идеи, но я бы предпочел интеграционные тесты, а не юнит-тесты для устаревшего кода. В обоих случаях вы должны быть уверены, что методы работают правильно, прежде чем добавлять какое-либо тестирование, но Integration Testing гарантирует, что общие ожидания методов работают в отличие от отдельных элементов кода.
Бритт Уэскотт,
5

Интеграционные тесты должны только проверить, что несколько компонентов работают вместе, как и ожидалось. Точность логики отдельных компонентов должна быть подтверждена модульными тестами.
Большинство методов имеют несколько возможных путей выполнения; Подумайте о if-then-else, входных переменных с неожиданными или просто неправильными значениями и т. д. Обычно разработчики склонны думать только о счастливом пути: нормальном пути, который не идет не так. Но что касается этих других путей, у вас есть два варианта: вы можете позволить своему конечному пользователю исследовать эти пути с помощью действий, которые они выполняют в пользовательском интерфейсе, и надеяться, что они не потерпят крах в вашем приложении, или вы можете написать модульные тесты, которые утверждают поведение этих других путей и принять меры, где это необходимо.

Стефан Биллиет
источник
4

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

Кроме того, большая ошибка, которую вы можете совершать, заключается в доверии

Find bug that may not be caught in integration test. e.g. masking/offsetting bugs.

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

Я полагаю, что канонические ссылки на интегрированные тесты - это посты JBrains:

http://www.jbrains.ca/permalink/integrated-tests-are-a-scam-part-1

в случае, если вы еще не прочитали их.

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

Хосе Рикардо
источник
Я действительно не понимаю эту статью. Таким образом, существует множество путей кода, и интеграционные тесты не могут охватить их все ... так? То же самое относится и к юнит-тестам, если ваши юниты не настолько тривиальны, как бессмысленные.
Кейси
Это больше похоже на аргумент против фиксации на покрытии кода, чем на то, что вы должны написать тонну модульных тестов и отказаться от интеграционных тестов.
Кейси
1

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

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

Мэтью Флинн
источник
3
Я не чувствую, что вы сделали дело. Интеграционные тесты также выявляют ошибки в коде, и на самом деле способны обнаруживать ошибки, которых нет в модульных тестах.
Кейси