Контекст:
В настоящее время я работаю над небольшим проектом на Python. Я обычно структурирую свои классы с помощью некоторых открытых методов, которые задокументированы, но в основном имеют дело с концепциями высокого уровня (то, что пользователь класса должен знать и использовать), а также с кучей скрытых (начиная с подчеркивания) методов, которые отвечают за сложная или низкоуровневая обработка.
Я знаю, что тесты необходимы, чтобы придать уверенность в коде и гарантировать, что любая последующая модификация не нарушит предыдущее поведение.
Проблема:
Чтобы создать общедоступные методы более высокого уровня на основе доверия, я обычно проверяю закрытые методы. Я нахожу более легким найти, ввела ли модификация кода регрессии и где. Это означает, что эти внутренние тесты могут выйти из строя при незначительных изменениях и должны быть исправлены / заменены
Но я также знаю , что модульное тестирование частного метод, по крайней мере спорная концепция или чаще рассматриваются как плохая практика. Причина в том, что следует проверять только публичное поведение ( ссылка )
Вопрос:
Я забочусь о следовании передовой практике и хотел бы понять:
- почему плохо использовать модульные тесты для закрытых / скрытых методов (каков риск)?
- Каковы лучшие практики, когда общедоступные методы могут использовать низкоуровневую и / или сложную обработку?
Precisions:
- это не как вопрос. Python не имеет истинной концепции конфиденциальности, и скрытые методы просто не перечислены, но могут использоваться, когда вы знаете их имя
- Меня никогда не учили правилам и шаблонам программирования: мои последние уроки были с 80-х годов ... В основном я учил языки методом проб и неудач и ссылками в Интернете (Stack Exchange - мой любимый в течение многих лет)
источник
Ответы:
Пара причин:
Как правило, когда вы испытываете желание протестировать закрытый метод класса, это запах проекта (класс айсберга, недостаточно открытых компонентов многократного использования и т. Д.). Почти всегда есть какая-то "большая" проблема в игре.
Вы можете проверить их через открытый интерфейс (именно так вы хотите их протестировать, потому что именно так клиент будет их вызывать / использовать). Вы можете получить ложное чувство безопасности, увидев зеленый свет на всех проходящих тестах для ваших личных методов. Намного лучше / безопаснее тестировать крайние случаи на ваших частных функциях через ваш публичный интерфейс.
Вы рискуете серьезным дублированием тестов (тесты, которые выглядят / выглядят очень похожими), тестируя частные методы. Это приводит к серьезным последствиям при изменении требований, так как будет проведено гораздо больше испытаний, чем необходимо. Это также может поставить вас в положение, в котором трудно провести рефакторинг из-за вашего набора тестов ... что является абсолютной иронией, потому что набор тестов поможет вам безопасно перестроить и реорганизовать!
Совет, если вы все еще испытываете желание протестировать закрытые части (не используйте их, если это вас беспокоит, и YMMV, но в прошлом это работало хорошо для меня): Иногда пишу модульные тесты для закрытых функций, просто чтобы убедиться, они работают именно так, как вы думаете, они могут быть ценными (особенно если вы новичок в языке). Однако после того, как вы убедитесь, что они работают, удалите тесты и всегда проверяйте, что общедоступные тесты являются надежными и отловят, если кто-то внесет вопиющие изменения в эту частную функцию.
Когда проверять частные методы: поскольку этот ответ стал (несколько) популярным, я чувствую себя обязанным упомянуть, что «лучшая практика» - это всегда только «лучшая практика». Это не значит, что вы должны делать это догматически или вслепую. Если вы думаете, что вам следует протестировать свои закрытые методы и иметь законную причину (например, вы пишете тесты характеристик для унаследованного приложения), то протестируйте свои закрытые методы . Конкретные обстоятельства всегда берут верх над любым общим правилом или передовой практикой. Просто знайте о некоторых вещах, которые могут пойти не так (см. Выше).
У меня есть ответ, подробно описывающий это в SO, который я не буду повторять здесь: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015
источник
bin_search(arr, item)
(public) иbin_search(arr, item, low, hi)
(private, есть много способов выполнить поиск поbin_search(arr, item)
Учитывая, что одной из основных целей модульных тестов является то, что вы можете провести рефакторинг внутренних компонентов вашей программы и затем убедиться, что вы не нарушили ее функциональность, это будет контрпродуктивно, если ваши модульные тесты будут работать на таком тонком уровне детализации, что любое изменение в коде программы требует от вас переписать свои тесты.
источник
Написание модульных тестов для частных методов связывает ваши модульные тесты с деталями реализации.
Модульные тесты должны проверять поведение класса на внешней поверхности класса (это общедоступный API). Модульные тесты не должны знать ничего о внутренностях класса. Написание модульных тестов на основе деталей реализации класса связывает ваши руки, когда приходит время проводить рефакторинг. Рефакторинг почти наверняка сломает эти тесты, потому что они не являются частью вашего стабильного API.
Тем не менее, почему , возможно , вы хотите , чтобы писать тесты для частных методов?
Существует естественная напряженность между модульными тестами и постепенным развитием. Разработчики программного обеспечения, использующие REPL (цикл чтения-проверки-печати), могут засвидетельствовать, насколько продуктивным может быть быстрое написание и тестирование небольших фрагментов функциональности по мере того, как вы «расширяете» класс или функцию. Единственный хороший способ сделать это в средах, управляемых модульным тестом, - это написать модульные тесты для частных методов, но в этом есть много разногласий. Для написания модульных тестов требуется время, вам нужен реальный метод для тестирования, и ваша инфраструктура тестирования должна поддерживать способность сохранять метод закрытым, чтобы он не загрязнял ваш внешний API.
В некоторых экосистемах, таких как C # и .NET, есть способы создания сред, подобных REPL (такие инструменты, как Linqpad, делают это), но их полезность ограничена, потому что у вас нет доступа к вашему проекту. Непосредственное окно в Visual Studio неудобно; у него все еще нет полного Intellisense, вы должны использовать в нем полностью определенные имена, и он запускает сборку каждый раз, когда вы его используете.
источник
По своему опыту я обнаружил, что модульное тестирование внутренних классов, методов обычно означает, что я должен вывести проверенные функции, классы. Чтобы создать еще один уровень абстракции.
Это приводит к лучшему соблюдению принципа единой ответственности.
источник
Я думаю, что это хороший вопрос, потому что он выставляет общую проблему при тестировании покрытия. Но хороший ответ должен сказать вам, что вопрос не совсем правильный, потому что, теоретически, вы не должны иметь возможность юнит-тестирования частных методов. Вот почему они частные .
Может быть, лучше задать вопрос: «Что мне делать, если я хочу протестировать частные методы?» и ответ вроде бы очевиден: вы должны показывать их так, чтобы сделать возможным тестирование. Теперь, это не обязательно означает, что вы должны просто сделать метод публичным и все. Скорее всего, вы захотите сделать более высокую абстракцию; Перейдите в другую библиотеку или API, чтобы вы могли выполнять свои тесты в этой библиотеке, не раскрывая эту функциональность в своем основном API.
Помните, что есть причина, по которой ваши методы имеют разные уровни доступности, и вы всегда должны думать о том, как ваши классы будут использоваться в конце.
источник