Примечание модератора: здесь уже размещено 39 ответов (некоторые из них были удалены). Прежде чем опубликовать свой ответ, подумайте, можете ли вы добавить что-то значимое в обсуждение. Вы, скорее всего, просто повторяете то, что уже сказал кто-то еще.
Иногда мне приходится делать закрытый метод в классе public, чтобы написать для него несколько модульных тестов.
Обычно это происходит потому, что метод содержит логику, совместно используемую другими методами в классе, и это более удобно для проверки логики самостоятельно, или возможна другая причина, если я хочу проверить логику, используемую в синхронных потоках, не беспокоясь о проблемах потоков ,
Есть ли другие люди делают это, потому что мне не нравится это делать ?? Лично я считаю, что бонусы перевешивают проблемы с публикацией метода, который на самом деле не предоставляет никаких услуг вне класса ...
ОБНОВИТЬ
Спасибо всем за ответы, кажется, заинтересовал людей. Я думаю, что общее мнение о том, что тестирование должно происходить через общедоступный API, так как это единственный способ использовать класс, и я с этим согласен. Пара случаев, которые я упомянул выше, где я делал это выше, была необычной, и я подумал, что польза от нее того стоила.
Однако я могу видеть, как все говорят, что этого никогда не должно случиться. И, думая об этом немного больше, я думаю, что изменение вашего кода для размещения тестов - плохая идея - в конце концов, я полагаю, что тестирование - это своего рода инструмент поддержки, а изменение системы на «поддержку инструмента поддержки», если хотите, является явным явлением. плохая практика
источник
@VisibileForTesting
аннотацию для создания таких методов - я рекомендую вам сделать это так, чтобы причина отсутствия метода былаprivate
должным образом задокументирована.Ответы:
Как правило, я обычно за рефакторинг «производственного» кода, чтобы его было проще тестировать. Тем не менее, я не думаю, что это был бы хороший звонок здесь. Хороший модульный тест (обычно) не должен заботиться о деталях реализации класса, а только о его видимом поведении. Вместо того, чтобы тестировать внутренние стеки, вы можете проверить, что класс возвращает страницы в том порядке, в котором вы ожидаете их после вызова
first()
илиlast()
.Например, рассмотрим этот псевдокод:
источник
Лично я бы предпочел модульное тестирование с использованием общедоступного API и, конечно же, никогда не делал бы закрытый метод общедоступным, просто чтобы его было легко тестировать.
Если вы действительно хотите протестировать приватный метод изолированно, в Java вы можете использовать Easymock / Powermock, чтобы позволить вам сделать это.
Вы должны быть прагматичными в этом, и вы должны также знать причины, по которым вещи трудно проверить.
« Прислушайтесь к тестам » - если это сложно протестировать, это говорит вам о вашем дизайне? Не могли бы вы провести рефакторинг, чтобы тест для этого метода был тривиален и легко охватывался тестированием через общедоступный API?
Вот что сказал Майкл Фезерс в « Эффективной работе с устаревшим кодом »
источник
Как уже говорили другие, это несколько подозрительно к тому, что юнит-тестирование является частным методом; модульное тестирование общедоступного интерфейса, а не частных деталей реализации.
Тем не менее, техникой, которую я использую, когда я хочу выполнить модульное тестирование чего-то частного в C #, является понижение уровня защиты доступности с частного на внутреннее, а затем пометка сборки модульного тестирования как дружественной сборки с использованием InternalsVisibleTo . Сборке модульного тестирования будет разрешено рассматривать внутренние объекты как общедоступные, но вам не нужно беспокоиться о случайном добавлении в вашу область общего пользования.
источник
Множество ответов предлагают только тестирование общедоступного интерфейса, но ИМХО это нереально - если метод делает что-то, что занимает 5 шагов, вы захотите протестировать эти пять шагов отдельно, а не все вместе. Это требует тестирования всех пяти методов, которые (кроме тестирования) могут быть иными
private
.Обычный способ тестирования «частных» методов состоит в том, чтобы дать каждому классу свой собственный интерфейс и создать «частные» методы
public
, но не включать их в интерфейс. Таким образом, они все еще могут быть протестированы, но они не раздувают интерфейс.Да, это приведет к раздуванию файлов и классов.
Да, это делает излишним
public
иprivate
спецификаторы.Да, это боль в заднице.
К сожалению, это одно из многих жертв, которые мы делаем, чтобы сделать код тестируемым . Возможно, будущий язык (или даже будущая версия C # / Java) будет иметь функции, которые сделают тестирование классов и модулей более удобным; но в то же время мы должны прыгать через эти обручи.
Некоторые утверждают, что каждый из этих шагов должен иметь свой собственный класс , но я не согласен - если они все совместно используют состояние, нет причин создавать пять отдельных классов, которые могли бы выполнять пять методов. Еще хуже, это приводит к раздутию файла и класса. Кроме того , он заражает общедоступный API вашего модуля - все эти классы обязательно должны быть,
public
если вы хотите протестировать их из другого модуля (или того, или включить тестовый код в тот же модуль, что означает доставку тестового кода с вашим продуктом) ,источник
Модульное тестирование должно проверять публичный контракт, единственный способ использования класса в других частях кода. Закрытый метод - это детали реализации, его не следует тестировать, поскольку публичный API работает правильно, реализация не имеет значения и может быть изменена без изменений в тестовых примерах.
источник
ИМО, вы должны писать свои тесты, не делая глубоких предположений о том, как ваш класс реализован внутри. Возможно, вы захотите реорганизовать его позже, используя другую внутреннюю модель, но при этом оставляя те же гарантии, что и в предыдущей реализации.
Помня об этом, я предлагаю вам сосредоточиться на проверке того, что ваш контракт остается в силе, независимо от того, какая внутренняя реализация у вашего класса в настоящее время. Тестирование ваших общедоступных API на основе свойств.
источник
Как насчет того, чтобы сделать его частным? Затем ваш тестовый код может увидеть его (и другие классы в вашем пакете), но он все еще скрыт от ваших пользователей.
Но на самом деле, вы не должны тестировать частные методы. Это детали реализации, а не часть контракта. Все, что они делают, должно быть покрыто вызовом открытых методов (если у них есть код, который не выполняется публичными методами, то это должно продолжаться). Если закрытый код слишком сложен, класс, вероятно, делает слишком много вещей и нуждается в рефакторинге.
Обнародование метода - это большое обязательство. Как только вы это сделаете, люди смогут его использовать, и вы больше не сможете их просто изменить.
источник
Обновление : я добавил более расширенный и более полный ответ на этот вопрос во многих других местах. Это можно найти в моем блоге .
Если мне когда-либо нужно сделать что-то публичное для тестирования, это обычно намекает на то, что тестируемая система не следует принципу единой ответственности . Следовательно, отсутствует класс, который следует ввести. После извлечения кода в новый класс сделайте его общедоступным. Теперь вы можете легко проверить, и вы следите за SRP. Ваш другой класс просто должен вызвать этот новый класс с помощью композиции.
Публикация методов / использование трюков языка, таких как маркировка кода как видимого для тестовых сборок, всегда должно быть последним средством.
Например:
Рефакторинг это путем введения объекта валидатора.
Теперь все, что нам нужно сделать, это проверить, что валидатор вызывается правильно. Фактический процесс проверки (ранее частная логика) может быть проверен в чистой изоляции. Там не будет необходимости в сложной настройке теста, чтобы гарантировать, что эта проверка проходит.
источник
Несколько отличных ответов. Одна вещь, о которой я не упомянул, это то, что при разработке через тестирование (TDD) частные методы создаются на этапе рефакторинга (см. « Метод извлечения» для примера шаблона рефакторинга), и поэтому у него уже должно быть необходимое покрытие тестами. , Если все сделано правильно (и, конечно, вы получите смешанный пакет мнений, когда дело доходит до правильности), вам не нужно беспокоиться о том, чтобы сделать закрытый метод публичным, просто чтобы вы могли его протестировать.
источник
Почему бы не разделить алгоритм управления стеком на служебный класс? Утилита класса может управлять стеками и предоставлять публичные средства доступа. Его модульные тесты могут быть сосредоточены на деталях реализации. Глубокие тесты для алгоритмически хитрых классов очень полезны в устранении крайних случаев и обеспечении охвата.
Тогда ваш текущий класс может чисто делегировать служебному классу, не раскрывая никаких деталей реализации. Его тесты будут относиться к требованию нумерации страниц, как рекомендовали другие.
источник
В Java также есть возможность сделать его закрытым (то есть отключить модификатор видимости). Если ваши модульные тесты находятся в том же пакете, что и тестируемый класс, они должны увидеть эти методы, и это немного безопаснее, чем сделать метод полностью общедоступным.
источник
Частные методы обычно используются как «вспомогательные» методы. Поэтому они только возвращают базовые значения и никогда не работают с конкретными экземплярами объектов.
У вас есть несколько вариантов, если вы хотите проверить их.
В качестве альтернативы вы можете создать новый класс с помощью вспомогательного метода в качестве открытого метода, если он достаточно хороший кандидат для нового класса.
Здесь есть очень хорошая статья .
источник
Если вы используете C #, вы можете сделать метод внутренним. Таким образом, вы не загрязняете общедоступный API.
Затем добавьте атрибут в dll
[сборка: InternalsVisibleTo ("MyTestAssembly")]
Теперь все методы видны в вашем проекте MyTestAssembly. Может быть, не идеально, но лучше, чем публичный метод публичного доступа, просто чтобы проверить его.
источник
Используйте отражение для доступа к закрытым переменным, если вам нужно.
Но на самом деле, вас не волнует внутреннее состояние класса, вы просто хотите проверить, что публичные методы возвращают то, что вы ожидаете в ситуациях, которые вы можете предвидеть.
источник
Я бы сказал, что это плохая идея, потому что я не уверен, получите ли вы какую-либо выгоду и потенциально проблемы в будущем. Если вы изменяете контракт вызовов, просто для того, чтобы протестировать закрытый метод, вы не тестируете класс на предмет его использования, а создаете искусственный сценарий, который вы никогда не собирались выполнять.
Более того, объявив метод общедоступным, можно сказать, что через шесть месяцев (после того, как вы забыли, что единственная причина сделать метод общедоступным - для тестирования), что вы (или если вы передали проект) кто-то совершенно другой выиграл не используйте его, что может привести к непредвиденным последствиям и / или кошмару обслуживания.
источник
с точки зрения модульного тестирования, вы не должны добавлять больше методов; Я считаю, что вам лучше создать тестовый пример только для вашего
first()
метода, который будет вызываться перед каждым тестом; то вы можете позвонить в несколько раз больше -next()
,previous()
иlast()
чтобы увидеть , если результаты соответствовали вашим ожиданиям. Я думаю, что если вы не добавите больше методов в свой класс (только для целей тестирования), вы будете придерживаться принципа «черного ящика» тестирования;источник
Сначала посмотрите, должен ли метод быть извлечен в другой класс и обнародован. Если это не так, сделайте его защищенным и в Java аннотируйте с помощью @VisibleForTesting .
источник
В своем обновлении вы говорите, что хорошо просто протестировать с помощью публичного API. Здесь на самом деле две школы.
Тестирование черного ящика
Школа черного ящика говорит, что класс должен рассматриваться как черный ящик, который никто не может увидеть в нем реализацию. Единственный способ проверить это - через публичный API - так же, как его использует пользователь класса.
тестирование белого ящика.
Школа белого ящика считает естественным использование знаний о реализации класса, а затем тестирование класса, чтобы убедиться, что оно работает так, как должно.
Я действительно не могу принять участие в обсуждении. Я просто подумал, что было бы интересно узнать, что есть два разных способа тестирования класса (или библиотеки, или чего-то еще).
источник
На самом деле есть ситуации, когда вы должны это сделать (например, когда вы реализуете некоторые сложные алгоритмы). Просто сделайте это пакетно-приватным, и этого будет достаточно. Но в большинстве случаев, вероятно, у вас слишком сложные классы, которые требуют разделения логики на другие классы.
источник
Частные методы, которые вы хотите тестировать изолированно, указывают на то, что в вашем классе есть еще одна «концепция». Извлеките эту «концепцию» в свой собственный класс и протестируйте ее как отдельную «единицу».
Взгляните на это видео для действительно интересного взгляда на эту тему.
источник
Вы никогда не должны позволять своим тестам диктовать ваш код. Я не говорю о TDD или других DD, я имею в виду именно то, что вы просите. Нужны ли вашему приложению эти методы, чтобы быть публичными? Если это так, то проверьте их. Если этого не произойдет, не публикуйте их только для тестирования. То же самое с переменными и другими. Пусть потребности вашего приложения диктуют код, и пусть ваши тесты проверяют, что потребность удовлетворена. (Опять же, я не имею в виду сначала тестирование или нет, я имею в виду изменение структуры классов для достижения цели тестирования).
Вместо этого вы должны «проверить выше». Проверьте метод, который вызывает закрытый метод. Но ваши тесты должны проверять потребности ваших приложений, а не ваши "решения по внедрению".
Например (псевдокод тела здесь);
Нет смысла проверять «добавить», вместо этого вы можете проверить «книги».
Никогда не позволяйте своим тестам принимать решения для разработки кода. Проверьте, что вы получите ожидаемый результат, а не то, как вы получите этот результат.
источник
Я склонен согласиться с тем, что бонусы, связанные с тестированием данного устройства, перевешивают проблемы увеличения видимости некоторых участников. Небольшое улучшение состоит в том, чтобы сделать его защищенным и виртуальным, а затем переопределить его в тестовом классе, чтобы показать его.
Или же, если это функциональность, которую вы хотите протестировать отдельно, не означает ли это, что в вашем дизайне отсутствует объект? Может быть, вы могли бы поместить его в отдельный тестируемый класс ... тогда ваш существующий класс просто делегирует экземпляр этого нового класса.
источник
Я обычно держу тестовые классы в том же проекте / сборке, что и тестируемые классы.
Так мне нужно только
internal
видимость, чтобы сделать функции / классы тестируемыми.Это несколько усложняет процесс сборки, который требует фильтрации тестовых классов. Я достигаю этого, называя все мои тестовые классы
TestedClassTest
и используя регулярные выражения для фильтрации этих классов.Это, конечно, относится только к C # / .NET-части вашего вопроса
источник
Я часто добавить метод , называемый что - то вроде
validate
,verify
,check
и т.д., к классу так , что его можно назвать , чтобы проверить внутреннее состояние объекта.Иногда этот метод помещается в блок ifdef (я пишу в основном на C ++), чтобы он не компилировался для выпуска. Но в выпуске часто полезно предоставлять методы валидации, которые обходят деревья объектов программы, проверяя вещи.
источник
У Guava есть аннотация @VisibleForTesting для маркировки методов, которые увеличили область действия (пакет или общедоступный), что они иначе. Я использую аннотацию @Private для того же.
В то время как публичный API должен быть протестирован, иногда бывает удобно и разумно заняться вещами, которые обычно не были бы публичными.
Когда:
похоже, что религия превосходит инженерию.
источник
Я обычно оставляю эти методы как
protected
и помещаю модульный тест в один и тот же пакет (но в другой проект или исходную папку), где они могут получить доступ ко всем защищенным методам, потому что загрузчик классов разместит их в одном и том же пространстве имен.источник
Нет, потому что есть лучшие способы снятия шкуры с этой кошки.
Некоторые модули модульного тестирования полагаются на макросы в определении класса, которые автоматически расширяются для создания хуков при построении в тестовом режиме. Очень стиль C, но это работает.
Более простая идиома OO состоит в том, чтобы сделать все, что вы хотите проверить, «защищенным», а не «приватным». Испытательный комплект наследует от тестируемого класса и может затем получить доступ ко всем защищенным членам
Или вы выбираете вариант «друг». Лично это особенность C ++, которая мне нравится меньше всего, потому что она нарушает правила инкапсуляции, но это необходимо для того, как C ++ реализует некоторые функции, так что, привет.
В любом случае, если вы проводите модульное тестирование, вам также может понадобиться ввести значения в эти элементы. Белая коробка текстовых сообщений вполне допустима. И это действительно будет сломать герметизацию.
источник
В .Net есть специальный класс под названием
PrivateObject
deigned, специально предназначенный для доступа к закрытым методам класса.Узнайте больше об этом на MSDN или здесь на переполнение стека
(Мне интересно, что никто до сих пор не упомянул об этом.)
Однако бывают ситуации, когда этого недостаточно, и в этом случае вам придется использовать рефлексию.
Еще я бы придерживался общей рекомендации не тестировать приватные методы, однако, как обычно, всегда есть исключения.
источник
Как широко отмечается в комментариях других, модульные тесты должны быть ориентированы на общедоступный API. Тем не менее, за и против и в сторону оправдания, вы можете вызывать частные методы в модульном тесте, используя рефлексию. Вы, конечно, должны убедиться, что ваша безопасность JRE позволяет это. Вызов закрытых методов - это то, что Spring Framework использует со своим ReflectionUtils (см.
makeAccessible(Method)
Метод).Вот небольшой пример класса с закрытым методом экземпляра.
И пример класса, который выполняет метод частного экземпляра.
Выполнение B выведет на печать
Doing something private.
Если вам это действительно нужно, отражение может использоваться в модульных тестах для доступа к частным методам экземпляра.источник
Лично у меня те же проблемы при тестировании частных методов, потому что некоторые инструменты тестирования ограничены. Нехорошо, когда ваш дизайн управляется ограниченными инструментами, если они не отвечают на ваши потребности, меняйте инструмент, а не дизайн. Поскольку вы просите C #, я не могу предложить хорошие инструменты тестирования, но для Java есть два мощных инструмента: TestNG и PowerMock, и вы можете найти соответствующие инструменты тестирования для платформы .NET
источник