Что не проверить, когда дело доходит до модульного тестирования?
В каких частях проекта написание юнит-тестов практически или практически невозможно? Доступ к данным? FTP?
Если на этот вопрос есть ответ, то охват 100% - это миф, не так ли?
21 ответ
Здесь я нашел (через взломанное что-то, что Майкл Фезерс говорит, что может быть ответом:
Он говорит,
Тест не является модульным тестом, если:
- Это говорит с базой данных
- Общается по сети
- Это касается файловой системы
- Он не может работать одновременно с другими вашими юнит-тестами
- Вы должны сделать специальные вещи в вашей среде (например, редактирование файлов конфигурации), чтобы запустить его.
Снова в той же статье он добавляет:
Как правило, модульные тесты должны быть небольшими, они тестируют метод или взаимодействие нескольких методов. Когда вы включаете базу данных, сокеты или доступ к файловой системе в свои модульные тесты, они больше не относятся к этим методам; они касаются интеграции вашего кода с этим другим программным обеспечением.
То, что 100% -ое покрытие - миф, это не значит, что 80% -ое покрытие бесполезно. Цель, конечно, на 100%, и между юнит-тестами и интеграционными тестами вы можете подойти к ней.
В модульном тестировании невозможно предсказать все совершенно странные вещи, которые ваши клиенты будут делать с продуктом. Как только вы начнете обнаруживать эти ошеломляющие извращения в вашем коде, обязательно откатите тесты для них обратно в набор тестов.
Достижение 100% покрытия кода почти всегда расточительно. Есть много ресурсов по этому вопросу.
Нет ничего невозможного для модульного тестирования, но всегда есть убывающая отдача. Возможно, не стоит проводить юнит-тестирование вещей, которые болезненны для юнит-теста.
Целью является не 100% покрытие кода и не 80% покрытие кода. Легкость написания модульного теста не означает, что вы должны его написать, а сложность написания модульных тестов не означает, что вам следует избегать усилий.
Целью любого теста является обнаружение видимых для пользователя проблем наиболее благоприятным способом.
Оправдывает ли общая стоимость разработки, обслуживания и диагностики проблем, отмеченных тестом (включая ложные срабатывания), проблемы, которые отлавливает конкретный тест?
Если проблема, с которой сталкивается тест, является "дорогой", тогда вы можете позволить себе попытаться выяснить, как ее протестировать, и поддерживать этот тест. Если проблема, с которой сталкивается тест, тривиальна, то написание (и поддержание!) Теста (даже при наличии изменений в коде) будет тривиальным.
Основная цель модульного теста - защитить разработчиков от ошибок реализации. Одно это должно указывать на то, что слишком большие усилия будут пустой тратой. После определенного момента есть лучшие стратегии для получения правильной реализации. Также после определенного момента видимые для пользователя проблемы возникают из-за правильной реализации неправильной вещи, которая может быть обнаружена только пользовательским уровнем или интеграционным тестированием.
Что бы вы не тестировали? Все, что не могло сломаться.
Когда дело доходит до покрытия кода, вы хотите стремиться к 100% кода, который вы фактически пишете - то есть вам не нужно тестировать код сторонней библиотеки или код операционной системы, поскольку этот код будет доставлен вам протестированным. Если это не так. В этом случае вы можете проверить это. Или, если есть известные ошибки, в этом случае вы можете проверить наличие ошибок, чтобы вы получили уведомление о том, когда они исправлены.
Модульное тестирование GUI также сложно, хотя, я думаю, не невозможно.
Доступ к данным возможен, потому что вы можете настроить тестовую базу данных.
Обычно "непроверяемые" данные - это FTP, электронная почта и так далее. Тем не менее, они, как правило, являются каркасными классами, на которые вы можете положиться, и поэтому не нужно проверять, прячете ли вы их за абстракцией.
Кроме того, 100% покрытия кода недостаточно.
"Что не нужно проверять, когда дело доходит до модульного тестирования?" * Фасоль только с добытчиками и сеттерами. Обоснование: обычно трата времени, которое лучше потратить на тестирование чего-то другого.
Большинство тестов, которые требуют огромных и дорогих (по стоимости ресурсов или времени вычислений) установок, являются интеграционными тестами. Модульные тесты должны (теоретически) тестировать только небольшие блоки кода. Индивидуальные функции.
Например, если вы тестируете функционал электронной почты, имеет смысл создать макет почтовой программы. Цель этого макета - убедиться, что ваш код правильно вызывает почтовую программу. Проверить, действительно ли ваше приложение отправляет почту, - это интеграционный тест.
Очень полезно различать юнит-тесты и интеграционные тесты. Юнит-тесты должны выполняться очень быстро. Должно быть легко выполнить все ваши юнит-тесты, прежде чем вы проверяете свой код.
Однако, если ваш набор тестов состоит из множества интеграционных тестов (которые устанавливают и разбирают базы данных и т. П.), Ваш тестовый запуск может легко превысить полчаса. В этом случае весьма вероятно, что разработчик не запустит все юнит-тесты, прежде чем он зарегистрируется.
Итак, чтобы ответить на ваш вопрос: выполните сетевые юнит-тесты, которые лучше реализовать как интеграционный тест (а также не тестируйте геттер / сеттер - это пустая трата времени;-)).
В модульном тестировании вы не должны тестировать ничего, что не принадлежит вашему устройству; Тестирование юнитов в их контексте - это другое дело. Это простой ответ.
Основное правило, которое я использую, состоит в том, что вы должны юнит-тестировать все, что касается границ вашего юнита (обычно класс или что-то еще, чем может быть ваш юнит), и издеваться над остальными. Нет необходимости проверять результаты, которые возвращает какой-либо запрос к базе данных, достаточно проверить, что ваш модуль выплевывает правильный запрос.
Это не означает, что вы не должны опускать то, что просто сложно проверить; Даже обработка исключений и проблемы параллелизма могут быть достаточно хорошо протестированы с помощью правильных инструментов.
Все, что не является полностью детерминированным, является нет-нет для модульного тестирования. Вы хотите, чтобы ваши модульные тесты ВСЕГДА проходили или не проходили с одинаковыми начальными условиями - если на это могут повлиять такие странности, как многопоточность, или случайная генерация данных, или время / даты, или внешние сервисы, то вам не следует покрывать это в своих модульных тестах., Время / даты - особенно неприятный случай. Обычно вы можете спроектировать код, чтобы иметь дату, с которой нужно работать (с помощью кода и тестов), вместо того, чтобы полагаться на функциональность в текущую дату и время.
Тем не менее, модульные тесты не должны быть единственным уровнем тестирования в вашем приложении. Достижение 100% покрытия модульными тестами часто является пустой тратой времени и быстро приносит убывающую отдачу.
Гораздо лучше иметь набор функциональных тестов более высокого уровня и даже интеграционные тесты, чтобы убедиться, что система работает правильно "после того, как все будет объединено", которые модульные тесты по определению не тестируют.
@GarryShutler
Я фактически проверяю электронную почту, используя поддельный SMTP-сервер (Wiser). Убедиться, что ваш код приложения правильный:
http://maas-frensch.com/peter/2007/08/29/unittesting-e-mail-sending-using-spring/
Нечто подобное может быть сделано для других серверов. В противном случае вы должны иметь возможность издеваться над API...
Кстати: 100% охват - это только начало... просто означает, что весь код фактически был выполнен один раз... ничего о крайних случаях и т. Д.
Я не согласен с ответом Quamrana относительно не тестирования стороннего кода. Это идеальное использование модульного теста. Что если в новой версии библиотеки появятся ошибки? В идеале, когда выпущена новая версия сторонней библиотеки, вы запускаете модульные тесты, которые представляют ожидаемое поведение этой библиотеки, чтобы убедиться, что она все еще работает как положено.
Вы можете проверить их, но они не будут модульными. Модульное тестирование - это то, что не пересекает границы, такое как пересечение провода, попадание в базу данных, запуск / взаимодействие с третьей стороной, прикосновение к непроверенной / устаревшей кодовой базе и т. Д.
Все, кроме этого, является интеграционным тестированием.
Очевидный ответ на вопрос в названии: вы не должны тестировать внутреннюю часть вашего API, не должны полагаться на поведение другого человека, вы не должны тестировать то, за что не несете ответственности.
Остального должно хватить только для того, чтобы вы могли писать в нем свой код, не больше, не меньше.
FTP, SMTP, I/O в целом должны быть протестированы с использованием интерфейса. Интерфейс должен быть реализован с помощью адаптера (для реального кода) и макета для модульного теста.
Ни один модульный тест не должен использовать реальный внешний ресурс (FTP-сервер и т. Д.)
Конфигурация - это еще один элемент, который очень трудно хорошо протестировать в модульных тестах. Интеграционные тесты и другие тесты должны проводиться против конфигурации. Это уменьшает избыточность тестирования и освобождает много времени. Попытка конфигурации модульного теста часто бывает несерьезной.
Все, что требует очень большой и сложной настройки. Конечно, вы можете протестировать ftp (клиент), но тогда вам нужно настроить ftp сервер. Для модульного теста вам нужна воспроизводимая тестовая установка. Если вы не можете предоставить это, вы не можете проверить это.
Конечно, 100% охват - хорошая цель при работе над большим проектом, но для большинства проектов исправление одной или двух ошибок перед развертыванием не обязательно требует времени для создания исчерпывающих модульных тестов.
Исчерпывающее тестирование таких вещей, как отправка форм, доступ к базе данных, доступ по FTP и т. Д. На очень детальном уровне, часто является пустой тратой времени; если написанное программное обеспечение не требует очень высокого уровня надежности (99,999%), слишком частое модульное тестирование может оказаться излишним и потратить в реальном времени.
FTP, электронную почту и т. Д. Вы можете проверить с помощью эмуляции сервера. Это сложно, но возможно.
Не тестируются некоторые ошибки обработки. В каждом коде есть обработка ошибок, которая никогда не может произойти. Например, в Java должно быть много исключений catch, потому что это часть интерфейса. Но используемый экземпляр никогда не бросит его. Или случай переключения по умолчанию, если для всех возможных случаев существует блок случая.
Конечно, часть ненужной обработки ошибок может быть удалена. Но есть ли ошибка кодирования в будущем, тогда это плохо.
Если код для установки состояния, необходимого для модульного теста, становится значительно более сложным, чем код, подлежащий тестированию, я стараюсь провести черту и найти другой способ проверить функциональность. В этот момент вы должны спросить, как вы узнали, что юнит-тест правильный!
Основная причина, по которой код модуля тестируется в первую очередь, заключается в проверке дизайна вашего кода. Можно получить 100% покрытие кода, но не без использования фиктивных объектов или какой-либо формы изоляции или внедрения зависимостей.
Помните, что модульные тесты предназначены не для пользователей, а для разработчиков и систем сборки, которые используются для проверки системы перед ее выпуском. Для этого модульные тесты должны выполняться очень быстро и иметь как можно меньше конфигураций и зависимостей. Попробуйте сделать как можно больше в памяти и избегать использования сетевых подключений из тестов.