Как выполнить модульное тестирование (с помощью xUnit) класса, у которого есть внутренние частные методы, поля или вложенные классы? Или функция, которая сделана частной за счет наличия внутренняя связь (static в C / C++) или находится в частном пространстве имен (анонимный)?
Кажется плохим изменять модификатор доступа для метода или функции только для того, чтобы иметь возможность запустить тест.
Проверьте статью Тестирование частных методов с помощью JUnit и SuiteRunner.




Лучший способ протестировать частный метод - использовать другой общедоступный метод. Если это невозможно сделать, то выполняется одно из следующих условий:
Кроме того, мы никогда не приближаемся к 100% покрытию кода, так почему бы не сосредоточить свое время на проверке качества методов, которые клиенты будут использовать напрямую.
@grinch Пятно на. Единственное, что вы получите от тестирования частных методов, - это отладочная информация, и для этого нужны отладчики. Если ваши тесты контракта класса имеют полное покрытие, тогда у вас есть вся необходимая информация. Частные методы - это деталь выполнение. Если вы их протестируете, вам придется менять тесты каждый раз, когда изменяется ваша реализация, даже если в контракте этого не происходит. В течение полного жизненного цикла программного обеспечения это, вероятно, будет стоить намного дороже, чем выгода, которую оно дает.
Обычно модульный тест предназначен для проверки открытого интерфейса класса или модуля. Следовательно, частные методы - это деталь реализации, которую вы не ожидаете тестировать явно.
Это лучший ответ ИМО, или, как обычно говорят, тестовое поведение, а не методы. Модульное тестирование не заменяет метрики исходного кода, инструменты статического анализа кода и обзоры кода. Если частные методы настолько сложны, что для них требуются отдельные тесты, то, вероятно, потребуется рефакторинг, а не дополнительные тесты.
так что не пишите частные методы, просто создайте 10 других небольших классов?
Нет, это в основном означает, что вы можете протестировать функциональность частных методов, используя открытый метод, который их вызывает.
Частные методы вызываются общедоступным методом, поэтому входные данные для ваших общедоступных методов также должны тестировать частные методы, которые вызываются этими общедоступными методами. Когда общедоступный метод дает сбой, это может быть сбой частного метода.
Верный факт. Проблема в том, что реализация более сложная, например, если один общедоступный метод вызывает несколько частных методов. Какой из них потерпел неудачу? Или, если это сложный частный метод, который нельзя раскрыть или передать в новый класс
Вы уже упомянули, почему следует тестировать частный метод. Вы сказали: «Тогда это могло быть», поэтому вы не уверены. ИМО, следует ли тестировать метод, ортогонален его уровню доступа.
Из этой статьи: Тестирование частных методов с помощью JUnit и SuiteRunner (Билл Веннерс), у вас в основном есть 4 варианта:
- Don't test private methods.
- Give the methods package access.
- Use a nested test class.
Use reflection.
@JimmyT., Зависит от того, для кого предназначен "производственный код". Я бы назвал производственным кодом код, созданный для приложений, размещенных в VPN, целевыми пользователями которых являются системные администраторы.
@Pacerier Что ты имеешь в виду?
5-й вариант, как упомянуто выше, - это тестирование общедоступного метода, вызывающего частный метод? Правильный?
@LorenzoLerate Я боролся с этим, потому что я занимаюсь встроенной разработкой и хочу, чтобы мое программное обеспечение было как можно меньше. Ограничения по размеру и производительность - единственная причина, о которой я могу думать.
Мне очень нравится тест с отражением: Здесь шаблон Method = targetClass.getDeclaredMethod (methodName, argClasses); method.setAccessible (правда); return method.invoke (targetObject, argObjects);
@ user2441441 то же самое, что и # 1 - не тестируйте частные методы
Я стараюсь не тестировать частные методы. В этом безумие. Лично я считаю, что вам следует тестировать только свои общедоступные интерфейсы (включая защищенные и внутренние методы).
Для Java я бы использовал отражение, так как мне не нравится идея изменять доступ к пакету в объявленном методе только ради тестирования. Однако я обычно просто тестирую общедоступные методы, которые также должны гарантировать правильную работу частных методов.
you can't use reflection to get private methods from outside the owner class, the private modifier affects reflection also
Это неправда. Вы, безусловно, можете, как упоминалось в Ответ Джема Катиккаса.
вы не можете использовать отражение, чтобы получить частные методы извне класса владельца, частный модификатор влияет на отражение также Здесь - хорошая статья по теме вопроса
@jmfsg В статье, на которую вы ссылаетесь, конкретно говорится: «Тестирование частных методов требует немного больше усилий, но мы все равно можем сделать это с помощью System.Reflection». (Очевидно, вам нужен ReflectionPermission, но обычно это не проблема.)
Update:
Some 10 years later perhaps the best way to test a private method, or any inaccessible member, is via
@Jailbreakfrom the Manifold framework.@Jailbreak Foo foo = new Foo(); // Direct, *type-safe* access to *all* foo's members foo.privateMethod(x, y, z); foo.privateField = value;This way your code remains type-safe and readable. No design compromises, no overexposing methods and fields for the sake of tests.
Если у вас что-то вроде устаревшего приложения Ява и вам не разрешено изменять видимость ваших методов, лучший способ протестировать частные методы - использовать отражение.
Внутри мы используем помощники для получения / установки переменных private и private static, а также для вызова методов private и private static. Следующие шаблоны позволят вам делать практически все, что связано с частными методами и полями. Конечно, вы не можете изменить переменные private static final через отражение.
Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);
А для полей:
Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
Notes:
1.TargetClass.getDeclaredMethod(methodName, argClasses)lets you look intoprivatemethods. The same thing applies forgetDeclaredField.
2. ThesetAccessible(true)is required to play around with privates.
Может быть полезно, если вы не знаете API, но если вам нужно тестировать частные методы таким образом, с вашим дизайном что-то не так. Как говорится на другом плакате, модульное тестирование должно проверять контракт класса: если контракт слишком широкий и создает слишком большую часть системы, тогда следует заняться дизайном.
Попробовав Cem Catikkas решение с использованием отражения для Java, я должен сказать, что это было более элегантное решение, чем я описал здесь. Однако, если вы ищете альтернативу использованию отражения и имеете доступ к тестируемому источнику, это все равно будет вариантом.
Есть возможные достоинства в тестировании частных методов класса, особенно с разработка через тестирование, где вы хотели бы разрабатывать небольшие тесты, прежде чем писать какой-либо код.
Создание теста с доступом к частным членам и методам может тестировать области кода, которые сложно настроить конкретно, с доступом только к общедоступным методам. Если общедоступный метод включает несколько этапов, он может состоять из нескольких частных методов, которые затем можно тестировать индивидуально.
Преимущества:
Недостатки:
Однако, если для непрерывного тестирования требуется этот метод, это может быть сигналом о том, что следует извлечь частные методы, которые можно протестировать традиционным, общедоступным способом.
Вот запутанный пример того, как это будет работать:
// Import statements and package declarations
public class ClassToTest
{
private int decrement(int toDecrement) {
toDecrement--;
return toDecrement;
}
// Constructor and the rest of the class
public static class StaticInnerTest extends TestCase
{
public StaticInnerTest(){
super();
}
public void testDecrement(){
int number = 10;
ClassToTest toTest= new ClassToTest();
int decremented = toTest.decrement(number);
assertEquals(9, decremented);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(StaticInnerTest.class);
}
}
}
Внутренний класс будет скомпилирован в ClassToTest$StaticInnerTest.
См. Также: Совет по Java 106: статические внутренние классы для развлечения и получения прибыли
Если вы пытаетесь протестировать существующий код, который не хотите или не можете изменить, отражение - хороший выбор.
Если дизайн класса по-прежнему гибкий, и у вас есть сложный частный метод, который вы хотите протестировать отдельно, я предлагаю вам выделить его в отдельный класс и протестировать этот класс отдельно. При этом не обязательно изменять публичный интерфейс исходного класса; он может внутренне создать экземпляр вспомогательного класса и вызвать вспомогательный метод.
Если вы хотите протестировать сложные условия ошибки, исходящие от вспомогательного метода, вы можете пойти еще дальше. Извлеките интерфейс из вспомогательного класса, добавьте общедоступный метод получения и установки к исходному классу, чтобы внедрить вспомогательный класс (используемый через его интерфейс), а затем внедрить фиктивную версию вспомогательного класса в исходный класс, чтобы проверить, как исходный класс отвечает на исключения от помощника. Этот подход также полезен, если вы хотите протестировать исходный класс без тестирования вспомогательного класса.
Если вы хотите протестировать частные методы устаревшего приложения, в котором вы не можете изменить код, один из вариантов для Java - jMockit, который позволит вам создавать макеты объекта, даже если они являются частными для класса.
Как часть библиотеки jmockit, у вас есть доступ к классу Deencapsulation, который упрощает тестирование частных методов: Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Я использую этот подход все время. Очень полезно
Когда у меня есть частные методы в классе, которые достаточно сложны, что я чувствую необходимость напрямую протестировать частные методы, это запах кода: мой класс слишком сложен.
Мой обычный подход к решению таких проблем - выделить новый класс, содержащий интересные элементы. Часто этот метод и поля, с которыми он взаимодействует, и, возможно, еще один или два метода могут быть извлечены в новый класс.
Новый класс предоставляет эти методы как «общедоступные», поэтому они доступны для модульного тестирования. Новый и старый классы теперь проще, чем исходный класс, что для меня здорово (мне нужно все упрощать, иначе я потеряюсь!).
Обратите внимание: я не предлагаю людям создавать классы, не используя свой мозг! Дело здесь в том, чтобы использовать возможности модульного тестирования, чтобы помочь вам найти новые хорошие классы.
Вопрос был в том, как тестировать частные методы. Вы говорите, что создадите для этого новый класс (и добавите гораздо больше сложности), а затем предложите не создавать новый класс. Так как же тестировать частные методы?
Я думаю, что если у вас есть метод, нет необходимости создавать другой класс только для того, чтобы протестировать этот метод. Я не думаю, что здесь был вопрос о дизайне классов. Я предполагаю, что автор сделал все, чтобы иметь правильный дизайн и раньше, поэтому, вводя другой класс для одного метода, вы просто увеличиваете сложность. Конечно, когда у вас будет достаточно сложная программа, явных ошибок не будет ..
@Dainius: я не предлагаю создавать новый класс исключительно, чтобы вы могли протестировать этот метод. Я полагаю, что написание тестов может помочь вам улучшить ваш дизайн: хороший дизайн легко проверить.
Но вы согласны с тем, что хороший OOD будет предоставлять (делать общедоступными) только те методы, которые необходимы для правильной работы этого класса / объекта? все остальные должны быть частными / protectec. Так что в некоторых частных методах будет некоторая логика, и тестирование этих методов IMO только улучшит качество программного обеспечения. Конечно, я согласен с тем, что если какой-то фрагмент кода слишком сложен, его следует разделить на отдельные методы / классы.
Здесь есть небольшая проблема с курицей и яйцом. Строго говоря, у вас должен быть код, который вы хотите сначала провести рефакторинг при тестировании, но чтобы протестировать код, вам сначала нужно поместить его в другой класс. Мне это кажется немного неоптимальным.
То, что вы описываете, является постоянной проблемой для всех, кто пытается выполнить модульное тестирование, и выходит далеко за рамки этого вопроса. Короткий ответ: «Прочтите Эффективная работа с устаревшим кодом Майкла Фезерса, а затем помните, что весь код является устаревшим кодом, через 5 минут после того, как вы его напишете.
@Danius: добавление нового класса в большинстве случаев снижает сложность. Просто измерьте это. Возможность тестирования в некоторых случаях борется с OOD. Современный подход основан на проверяемости.
Вот опасное высказывание: «достаточно сложное, что я чувствую необходимость протестировать». Если вы чувствуете необходимость протестировать только «достаточно сложный» код, вы делаете это неправильно. Вы не поверите, как часто тесты простых геттеров и сеттеров спасают положение.
@Charles Но, конечно, все частные методы уже протестированы с помощью общедоступных методов. Утверждение должно гласить: «достаточно сложный, чтобы я чувствовал необходимость протестировать его в изоляции».
Для меня первая строка этой записи была довольно поучительной. Если алгоритм слишком сложен и заслуживает тестирования, чтобы уменьшить шум в других тестах, с этого момента я буду рассматривать Шаблон стратегии. Рассмотрение не означает использование, но, безусловно, стоит взглянуть.
Это точно, как использовать отзывы о ваших тестах для улучшения вашего дизайна! Послушайте свои тесты. если их сложно написать, это говорит вам кое-что важное о вашем коде!
«Когда у меня есть частные методы в классе, которые достаточно сложны, что я чувствую необходимость напрямую протестировать частные методы, это запах кода» - иногда введение дополнительных классов делает вашу логику излишне непонятной и разбросанной по разным местам.
@JohnDonn Конечно. Но «Запах кода» не означает «вы должны изменить код следующим образом». Это означает «вот намек на место, где вы могли бы улучшить ситуацию». Вам решать, найти правильное изменение / судить, стоит ли ваша идея по улучшению, и решить, подходящее ли время для реализации этого изменения.
Тестировать частные методы - это нормально. Комментарии о том, что если мы тестируем частные методы, дизайн плохой, являются иррациональными. Можно применять тестирование на разных уровнях: на внутреннем уровне класса (частные / защищенные методы), уровне класса (общедоступные методы), уровне модуля, уровне системы и т. д.
Как было предложено многими выше, хороший способ - протестировать их через ваши общедоступные интерфейсы.
Если вы это сделаете, рекомендуется использовать инструмент покрытия кода (например, Emma), чтобы увидеть, действительно ли ваши частные методы выполняются из ваших тестов.
Не стоит косвенно тестировать! Не только прикосновение через покрытие; проверьте, что ожидаемый результат доставлен!
Если вы используете JUnit, взгляните на junit-аддоны. Он имеет возможность игнорировать модель безопасности Java и получать доступ к частным методам и атрибутам.
Во-первых, я задам этот вопрос: зачем вашим частным участникам нужно изолированное тестирование? Неужели они настолько сложны, что обеспечивают такое сложное поведение, что требуют тестирования отдельно от публичной поверхности? Это модульное тестирование, а не тестирование «строчки кода». Не переживайте по мелочам.
Если они настолько велики, достаточно велики, что каждый из этих частных членов представляет собой «единицу» большой сложности - рассмотрите возможность рефакторинга таких частных членов из этого класса.
Если рефакторинг неприемлем или невозможен, можете ли вы использовать шаблон стратегии для замены доступа к этим частным функциям-членам / классам-членам при модульном тестировании? При модульном тестировании стратегия обеспечит дополнительную проверку, но в сборках релизов это будет простой сквозной переход.
Потому что часто конкретный фрагмент кода из общедоступного метода реорганизуется во внутренний частный метод и на самом деле является важной частью логики, в которой вы могли ошибаться. Вы хотите протестировать это независимо от общедоступного метода
Даже самый короткий код иногда без модульного теста неверен. Просто попробуйте вычислить разницу между двумя географическими углами. 4 строки кода, и большинство из них не сможет исправить это с первого раза. Такие методы нуждаются в модульном тестировании, потому что они составляют основу надежного кода. (Такой полезный код тоже может быть общедоступным; менее полезный защищенный
Тестирование частных методов нарушает инкапсуляцию вашего класса, потому что каждый раз, когда вы меняете внутреннюю реализацию, вы нарушаете клиентский код (в данном случае тесты).
Так что не тестируйте частные методы.
unit test и src code - это пара. Если вы измените src, возможно, вам придется изменить unit test. В этом смысл теста юнит. Они должны гарантировать, что все работает, как прежде. и это нормально, если они сломаются, если вы измените код.
Не согласны с тем, что модульный тест должен измениться при изменении кода. Если вам приказано добавить функциональность к классу без изменения существующей функциональности класса, то существующие модульные тесты должны пройти даже после того, как вы изменили свой код. Как упоминает Питер, модульные тесты должны тестировать интерфейс, а не то, как это делается внутри. В разработке через тестирование модульные тесты создаются до написания кода, чтобы сосредоточиться на интерфейсе класса, а не на том, как он решается внутри.
Как говорили другие ... не тестируйте частные методы напрямую. Вот несколько мыслей:
Запустите покрытие кода модульными тестами. Если вы видите, что методы не полностью протестированы, добавьте тесты, чтобы увеличить охват. Стремитесь к 100% покрытию кода, но знайте, что вы, вероятно, этого не получите.
Вверх для покрытия кода. Независимо от того, какая логика находится в частном методе, вы все равно вызываете эту логику через общедоступный метод. Инструмент покрытия кода может показать вам, какие части покрываются тестом, поэтому вы можете увидеть, протестирован ли ваш частный метод.
Я видел классы, в которых единственным общедоступным методом является main [], и они открывают графический интерфейс, а также подключают базу данных и пару веб-серверов по всему миру. Легко сказать «не тестируйте косвенно» ...
Вы можете отключить ограничения доступа Java для отражения, чтобы частное ничего не значило.
Это делает вызов setAccessible(true).
Единственное ограничение состоит в том, что ClassLoader может запретить вам это делать.
См. Подрыв защиты доступа Java для модульного тестирования (Росс Бертон), чтобы узнать, как это сделать на Java.
В C# вы могли бы использовать System.Reflection, хотя в Java я не знаю. Хотя я все равно чувствую желание ответить на этот вопрос, поскольку, если вы «чувствуете, что вам нужно провести модульное тестирование частных методов», я предполагаю, что есть что-то еще, что не так ...
Я бы серьезно подумала о том, чтобы снова взглянуть на свою архитектуру свежим взглядом ...
Что, если ваши тестовые классы находятся в том же пакете, что и класс, который должен быть протестирован?
Но, конечно, в другом каталоге, src и classes для вашего исходного кода, test/src и test/classes для ваших тестовых классов. И пусть classes и test/classes будут в вашем пути к классам.
тесты могут находиться в разных папках (например, src / test / java и код приложения в src / main / java), но пакет должен быть таким же.
Раньше я использовал отражение для этого для Java, и, на мой взгляд, это было большой ошибкой.
Строго говоря, нет должен писать модульные тесты, которые напрямую тестируют частные методы. То, что вы тестируете должен, - это публичный контракт, который класс имеет с другими объектами; вам никогда не следует напрямую тестировать внутреннее устройство объекта. Если другой разработчик хочет внести небольшое внутреннее изменение в класс, которое не влияет на публичный контракт классов, он должен изменить ваш тест на основе отражения, чтобы убедиться, что он работает. Если вы делаете это неоднократно на протяжении всего проекта, модульные тесты перестают быть полезным измерением состояния кода и начинают мешать разработке и раздражать команду разработчиков.
Вместо этого я рекомендую использовать инструмент покрытия кода, такой как Cobertura, чтобы убедиться, что модульные тесты, которые вы пишете, обеспечивают достойное покрытие кода в частных методах. Таким образом, вы косвенно проверяете, что делают частные методы, и поддерживаете более высокий уровень гибкости.
+1 к этому. На мой взгляд, это лучший ответ на вопрос. Тестируя частные методы, вы тестируете реализацию. Это противоречит цели модульного тестирования, которое заключается в проверке входов / выходов контракта класса. Тест должен Только знать о реализации достаточно, чтобы имитировать методы, которые он вызывает в своих зависимостях. Ничего больше. Если вы не можете изменить свою реализацию без изменения теста, скорее всего, ваша стратегия тестирования плохая.
@Colin M Это не совсем то, о чем он спрашивает;) Пусть он сам решает, вы не знаете проект.
Не совсем так. Может потребоваться много усилий, чтобы протестировать небольшую часть частного метода через общедоступный метод, использующий его. Открытый метод может потребовать значительной настройки, прежде чем вы дойдете до линии, вызывающей ваш частный метод.
Я согласен. Вы должны проверить, выполняется ли договор. Не стоит проверять, как это делается. Если контакт выполняется без достижения 100% покрытия кода (в частных методах), это может быть мертвый или бесполезный код.
Всего два примера того, где я хотел бы протестировать частный метод:
SecurityManager не настроен для предотвращения этого).Я понимаю идею только тестирования "контракта". Но я не вижу, чтобы кто-то мог выступать за то, чтобы на самом деле не тестировать код - ваш опыт может отличаться.
Так что мой компромисс заключается в усложнении JUnits с отражением, а не в компрометации моей безопасности и SDK.
Хотя вы никогда не должны делать метод общедоступным только для его тестирования, private - не единственная альтернатива. Модификатор доступа не имеет значения package private и означает, что вы можете тестировать его, пока ваш модульный тест находится в том же пакете.
@ngreen, это все правда, но вопрос не в этом. ОП спрашивает о тестировании частных методов. По умолчанию это не то же самое, что и частный; как указано, вы можете легко увидеть метод доступа по умолчанию из класса, просто объявив этот класс в том же пакете. С частным доступом вам потребуется отражение, а это совсем другая игра.
Я комментировал ваш ответ, в частности пункт 1, а не OP. Нет необходимости делать метод закрытым только потому, что вы не хотите, чтобы он был общедоступным.
@ngreen true thx - я поленился со словом "паблик". Я обновил ответ, чтобы включить общедоступный, защищенный, по умолчанию (и упомянуть об отражении). Я пытался сказать, что есть веская причина для того, чтобы какой-то код был секретным, однако это не должно помешать нам его протестировать.
Спасибо, что дали всем реальные примеры. Большинство ответов хороши, но с теоретической точки зрения. В реальном мире нам действительно нужно скрыть реализацию из контракта, и нам все еще нужно ее протестировать. Читая все это, я думаю, может быть, это должен быть совершенно новый уровень тестирования с другим названием.
Еще один пример - Сканеры HTML-парсеры. Необходимость разобрать всю html-страницу на объекты домена требует тонны тонкой логики проверки небольших частей структуры. Имеет смысл разбить это на методы и вызвать его из одного общедоступного метода, но не имеет смысла делать все более мелкие методы, составляющие его, общедоступными, и нет особого смысла создавать 10 классов на каждой HTML-странице.
Чтобы протестировать устаревший код с большими и необычными классами, часто бывает очень полезно иметь возможность протестировать один закрытый (или общедоступный) метод, который я пишу прямо сейчас.
Я использую junitx.util.PrivateAccessor-пакет для Java. Множество полезных однострочников для доступа к приватным методам и приватным полям.
import junitx.util.PrivateAccessor;
PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);
Обязательно загрузите весь пакет JUnit-addons (sourceforge.net/projects/junit-addons), а не PrivateAccessor проекта, рекомендованного Source Forge.
И убедитесь, что когда вы используете Class в качестве первого параметра в этих методах, вы получаете доступ только к членам static.
Ответ от Страница часто задаваемых вопросов JUnit.org:
But if you must...
If you are using JDK 1.3 or higher, you can use reflection to subvert the access control mechanism with the aid of the PrivilegedAccessor. For details on how to use it, read this article.
If you are using JDK 1.6 or higher and you annotate your tests with @Test, you can use Dp4j to inject reflection in your test methods. For details on how to use it, see this test script.
P.S. Я главный участник Dp4j, спросите мне, если вам нужна помощь. :)
Я тестирую только общедоступный интерфейс, но, как известно, я защищал определенные частные методы, поэтому я могу либо полностью их имитировать, либо добавить дополнительные шаги, специфичные для целей модульного тестирования. Общий случай - перехватить флаги, которые я могу установить из модульного теста, чтобы определенные методы намеренно вызывали исключение, чтобы иметь возможность тестировать пути сбоя; код запуска исключения находится только в тестовом пути в переопределенной реализации защищенного метода.
Я минимизирую необходимость в этом и всегда документирую точные причины, чтобы избежать путаницы.
Я не уверен, что это хороший метод, но я разработал следующий шаблон для модульного тестирования частных методов:
Я не изменяю видимость метода, который хочу протестировать, и не добавляю дополнительный метод. Вместо этого я добавляю дополнительный общедоступный метод для каждого частного метода, который хочу протестировать. Я называю этот дополнительный метод Test-Port и обозначаю его префиксом t_. Затем этот метод Test-Port просто обращается к соответствующему частному методу.
Кроме того, я добавляю логический флаг к методу Test-Port, чтобы решить, предоставляю ли я доступ к частному методу через метод Test-Port извне. Затем этот флаг устанавливается глобально в статическом классе, где я помещаю, например, другие глобальные настройки приложения. Таким образом, я могу включать и отключать доступ к частным методам в одном месте, например. в соответствующем модульном тесте.
Groovy имеет ошибка / функция, с помощью которого вы можете вызывать частные методы, как если бы они были общедоступными. Так что, если вы можете использовать Groovy в своем проекте, это вариант, который вы можете использовать вместо отражения. Посмотрите, например, на эта страница.
Это взлом, а не реальное решение.
@givanse Я не согласен ... это одна из причин, по которой тестирование Java-кода с использованием Groovy настолько эффективно.
JML имеет синтаксис аннотации комментариев spec_public, который позволяет вам указать метод как общедоступный во время тестов:
private /*@ spec_public @*/ int methodName(){
...
}
Этот синтаксис обсуждается в http://www.eecs.ucf.edu/~leavens/JML/jmlrefman/jmlrefman_2.html#SEC12. Также существует программа, переводящая спецификации JML в тесты JUnit. Я не уверен, насколько хорошо это работает и каковы его возможности, но, похоже, в этом нет необходимости, поскольку JML сам по себе является жизнеспособной средой тестирования.
Сегодня я запустил библиотеку Java, чтобы помочь в тестировании частных методов и полей. Он был разработан с учетом Android, но его действительно можно использовать для любого проекта Java.
Если у вас есть код с частными методами, полями или конструкторами, вы можете использовать BoundBox. Он делает именно то, что вы ищете. Ниже приведен пример теста, который обращается к двум частным полям активности Android для его проверки:
@UiThreadTest
public void testCompute() {
// Given
boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());
// When
boundBoxOfMainActivity.boundBox_getButtonMain().performClick();
// Then
assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}
BoundBox упрощает тестирование частных / защищенных полей, методов и конструкторов. Вы даже можете получить доступ к тому, что скрыто по наследству. Действительно, BoundBox нарушает инкапсуляцию. Это даст вам доступ ко всему этому через отражение, НО все проверяется во время компиляции.
Он идеально подходит для тестирования устаревшего кода. Используйте его осторожно. ;)
https://github.com/stephanenicolas/boundbox
Только что попробовал, BoundBox - простое и элегантное решение!
Частные методы потребляются общедоступными. В противном случае это мертвый код. Вот почему вы тестируете общедоступный метод, утверждая ожидаемые результаты общедоступного метода и, следовательно, потребляемые им частные методы.
Перед запуском модульных тестов для общедоступных методов тестирование частных методов следует тестировать путем отладки.
Их также можно отлаживать с помощью разработки, управляемой тестированием, отладки модульных тестов до тех пор, пока не будут выполнены все ваши утверждения.
Я лично считаю, что лучше создавать классы с использованием TDD; создание заглушек общедоступных методов, а затем создание модульных тестов с все утверждений, определенных заранее, поэтому ожидаемый результат метода определяется до того, как вы его запрограммируете. Таким образом, вы не пойдете по ложному пути, заставляя утверждения модульного теста соответствовать результатам. Тогда ваш класс станет надежным и соответствует требованиям, когда все ваши модульные тесты пройдут.
Хотя это правда, это может привести к очень сложным тестам - лучше проверять один метод за раз (модульное тестирование), чем целую их группу. Не ужасное предложение, но я думаю, что здесь есть лучшие ответы - это должно быть последнее средство.
Вот моя общая функция для проверки частных полей:
protected <F> F getPrivateField(String fieldName, Object obj)
throws NoSuchFieldException, IllegalAccessException {
Field field =
obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (F)field.get(obj);
}
Доступ к частному методу можно получить только в том же классе. Таким образом, невозможно протестировать «частный» метод целевого класса из любого тестового класса. Выход состоит в том, что вы можете выполнить модульное тестирование вручную или изменить свой метод с «частного» на «защищенный».
И тогда доступ к защищенному методу можно будет получить только в том же пакете, где определен класс. Итак, тестирование защищенного метода целевого класса означает, что нам нужно определить ваш тестовый класс в том же пакете, что и целевой класс.
Если все вышеперечисленное не соответствует вашим требованиям, используйте путь отражения для доступа к закрытому методу.
Вы смешиваете «защищенный» с «дружественным». Доступ к защищенному методу может получить только класс, объекты которого назначаются целевому классу (то есть подклассам).
На самом деле в Java нет «Friendly», это термин «Package», и на это указывает отсутствие модификатора private / public / protected. Я бы просто исправил этот ответ, но есть действительно хороший ответ, который уже говорит об этом, поэтому я просто рекомендую удалить его.
Я чуть не проголосовал против, все время думая: «Это просто неправильный ответ». пока я не добрался до самого последнего предложения, которое противоречит остальному ответу. Последнее предложение должно было быть первым.
Если вы используете Spring, ReflectionTestUtils предоставляет несколько удобных инструментов, которые помогут здесь с минимальными усилиями. Например, чтобы настроить имитацию частного члена без необходимости добавлять нежелательный публичный сеттер:
ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);
Недавно у меня возникла эта проблема, и я написал небольшой инструмент под названием Отмычка, который позволяет избежать проблем явного использования API отражения Java, два примера:
Вызов методов, например private void method(String s) - отражение Java
Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");
Вызов методов, например private void method(String s) - от Picklock
interface Accessible {
void method(String s);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");
Поля настройки, например private BigInteger amount; - отражение Java
Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));
Поля настройки, например private BigInteger amount; - от Picklock
interface Accessible {
void setAmount(BigInteger amount);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));
В Spring Framework вы можете тестировать частные методы, используя этот метод:
ReflectionTestUtils.invokeMethod()
Например:
ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");
На данный момент самое чистое и краткое решение, но только если вы используете Spring.
Другой подход, который я использовал, - это изменить частный метод на частный или защищенный, а затем дополнить его аннотацией @VisibleForTesting библиотеки Google Guava.
Это скажет любому, кто использует этот метод, проявлять осторожность и не обращаться к нему напрямую даже в пакете. Также тестовый класс не обязательно должен находиться в том же пакете физически, но в том же пакете в папке тест.
Например, если тестируемый метод находится в src/main/java/mypackage/MyClass.java, ваш тестовый вызов должен быть помещен в src/test/java/mypackage/MyClassTest.java. Таким образом, вы получили доступ к методу тестирования в своем тестовом классе.
Я не знал об этом, это интересно, я все еще думаю, что если вам нужна такая аннотация, у вас есть проблемы с дизайном.
Для меня это все равно что сказать, что вместо того, чтобы дать одноразовый ключ своему начальнику пожарной охраны для проверки учения по пожарной безопасности, вы оставляете входную дверь незапертой с табличкой на двери с надписью «разблокирована для проверки», если вы не из наша компания, пожалуйста, не входите ».
@FrJeremyKrieg - что это значит?
@ MasterJoe2: цель испытания на огнестойкость - повысить безопасность вашего здания от риска возгорания. Но вы должны предоставить надзирателю доступ к своему зданию. Если вы навсегда оставите входную дверь незапертой, вы увеличите риск несанкционированного доступа и сделаете ее меньше безопасной. То же самое и с шаблоном VisibleForTesting - вы увеличиваете риск несанкционированного доступа, чтобы снизить риск «пожара». Лучше предоставлять одноразовый доступ для тестирования только тогда, когда это необходимо (например, с помощью отражения), чем оставлять его постоянно разблокированным (не закрытым).
См. Пример ниже;
Следует добавить следующий оператор импорта:
import org.powermock.reflect.Whitebox;
Теперь вы можете напрямую передать объект, который имеет закрытый метод, имя вызываемого метода и дополнительные параметры, как показано ниже.
Whitebox.invokeMethod(obj, "privateMethod", "param1");
Быстрое добавление к комментарию @Cem Catikka при использовании ExpectedException:
Имейте в виду, что ваше ожидаемое исключение будет заключено в InvocationTargetException, поэтому, чтобы добраться до вашего исключения, вам нужно будет выбросить причину полученного вами InvocationTargetException. Что-то вроде (тестирование частного метода validateRequest () на BizService):
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired(required = true)
private BizService svc;
@Test
public void testValidateRequest() throws Exception {
thrown.expect(BizException.class);
thrown.expectMessage(expectMessage);
BizRequest request = /* Mock it, read from source - file, etc. */;
validateRequest(request);
}
private void validateRequest(BizRequest request) throws Exception {
Method method = svc.getClass().getDeclaredMethod("validateRequest", BizRequest.class);
method.setAccessible(true);
try {
method.invoke(svc, request);
}
catch (InvocationTargetException e) {
throw ((BizException)e.getCause());
}
}
Я бы посоветовал вам немного реорганизовать свой код. Когда вам нужно начать думать об использовании отражения или других вещей, просто для тестирования вашего кода, с вашим кодом что-то не так.
Вы упомянули разные типы проблем. Начнем с частных полей. В случае частных полей я бы добавил новый конструктор и ввел в него поля. Вместо этого:
public class ClassToTest {
private final String first = "first";
private final List<String> second = new ArrayList<>();
...
}
Я бы использовал это:
public class ClassToTest {
private final String first;
private final List<String> second;
public ClassToTest() {
this("first", new ArrayList<>());
}
public ClassToTest(final String first, final List<String> second) {
this.first = first;
this.second = second;
}
...
}
Это не будет проблемой даже с некоторым устаревшим кодом. Старый код будет использовать пустой конструктор, и, если вы спросите меня, реорганизованный код будет выглядеть чище, и вы сможете вводить необходимые значения в тест без отражения.
Теперь о частных методах. По моему личному опыту, когда вам нужно заглушить частный метод для тестирования, этот метод не имеет ничего общего с этим классом. Обычным шаблоном в этом случае будет сворачивать внутри интерфейса, такого как Callable, а затем вы передадите этот интерфейс также в конструктор (с этим трюком с несколькими конструкторами):
public ClassToTest() {
this(...);
}
public ClassToTest(final Callable<T> privateMethodLogic) {
this.privateMethodLogic = privateMethodLogic;
}
По большей части все, что я написал, похоже на шаблон внедрения зависимостей. По моему личному опыту, это действительно полезно при тестировании, и я думаю, что такой код чище и его будет легче поддерживать. То же самое я бы сказал о вложенных классах. Если вложенный класс содержит тяжелую логику, было бы лучше, если бы вы переместили его как частный класс пакета и внедрили в класс, который в нем нуждается.
Есть также несколько других шаблонов проектирования, которые я использовал при рефакторинге и поддержке устаревшего кода, но все зависит от того, какие варианты кода нужно тестировать. Использование отражения в основном не проблема, но когда у вас есть корпоративное приложение, которое тщательно протестировано и тесты запускаются перед каждым развертыванием, все становится очень медленно (это просто раздражает, и мне не нравятся такие вещи).
Также есть сеттер-инъекция, но я бы не рекомендовал ее использовать. Я лучше остановлюсь на конструкторе и инициализирую все, когда это действительно необходимо, оставив возможность для внедрения необходимых зависимостей.
Не согласен с впрыском ClassToTest(Callable). Это усложняет ClassToTest. Будь проще. Кроме того, это требует, чтобы кто-то еще сообщил ClassToTest что-то, чем ClassToTest должен быть хозяином. Здесь есть место для введения логики, но это не то. Вы только что сделали класс труднее, а не проще.
Кроме того, предпочтительнее, если ваш метод тестирования X не увеличивает сложность X до такой степени, что это может вызвать больше проблем ... и, следовательно, требует дополнительного тестирования ... которое, если вы внедрили способ, который может вызвать больше проблем. ... (это не бесконечные циклы; каждая итерация, вероятно, меньше предыдущей, но все равно раздражает)
Не могли бы вы объяснить, почему это затрудняет поддержку класса ClassToTest? На самом деле это упрощает обслуживание вашего приложения. Что вы предлагаете создать новый класс для каждого другого значения, которое вам понадобится в first и «вторых» переменных?
Методика тестирования не усложняет. Просто ваш класс плохо написан, настолько плохо, что его невозможно протестировать.
Сложнее поддерживать, потому что класс сложнее. class A{ A(){} f(){} } проще, чем class A { Logic f; A(logic_f) } class B { g() { new A( logic_f) } }. Если бы это было неправдой, и если бы было правдой, что предоставление логики класса в качестве аргументов конструктора было легче поддерживать, тогда мы бы передали всю логику как аргументы конструктора. Я просто не понимаю, как можно утверждать, что class B{ g() { new A( you_dont_know_your_own_logic_but_I_do ) } } когда-либо упрощает обслуживание A. Бывают случаи, когда подобное введение имеет смысл, но я не считаю ваш пример "более простым в обслуживании".
Что касается «что вы предлагаете создать новый класс для каждого другого необходимого вам значения» ... я не понимаю, как это следует. Нет. Я говорю, что все будет проще. class A { M m; N n; f() { } } супер просто. Не отказывайтесь от этого, если у вас нет причины. Если вам нужно было протестировать приватных членов, конечно, прекрасно; Я просто не понимаю, как вы можете утверждать, что его «легче поддерживать». Внедрение зависимостей упрощает обслуживание только тогда, когда оно снижает сложность (разделяет классы, упрощает отслеживание графиков / диаграмм, что нибудь), чего не было в вашем примере.
Обратите внимание: я не утверждаю, что ваш фактический ответ неверен. Фактически, предоставление логики извне может дать ответ на исходный вопрос. Я комментирую только конкретное утверждение в ответе, утверждение, от которого ответ не зависит.
На самом деле, при третьем (или четвертом?) Чтении я вижу, что, возможно, упустил момент. В вашем public ClassToTest() { this(...); } без аргументов подразумевается, что обычно используется конструктор класса без аргументов, что он может предоставить свою собственную логику, известную внутри класса, а другой конструктор использовал Только для тестирования (или если B хочет изменить свой поведение по какой-то причине; удобный побочный эффект)? Я не уловил, что первая пара перечитывает.
Да, теперь я понял. Тот факт, что у вас нет реализации по умолчанию, известной изнутри ClassToTest, сбил меня с толку, но у вас есть это подразумевается эллипсом. По какой-то причине я изначально думал, что вы предполагаете, что логика должна поставляться только извне. Для таких лохов, как я, не могли бы вы просто отредактировать "...", сделав что-то вроде "defaultLogic"; при условии, что я правильно вас понимаю.
Вы можете использовать PowerMockito для установки возвращаемых значений для частных полей и частных методов, которые вызываются / используются в частном методе, который вы хотите протестировать:
Например. Установка возвращаемого значения для частного метода:
MyClient classUnderTest = PowerMockito.spy(new MyClient());
//Set expected return value
PowerMockito.doReturn(20).when(classUnderTest, "myPrivateMethod", anyString(), anyInt());
//This is very important otherwise it will not work
classUnderTest.myPrivateMethod();
//Setting private field value as someValue:
Whitebox.setInternalState(classUnderTest, "privateField", someValue);
Затем, наконец, вы можете проверить, что ваш частный тестируемый метод возвращает правильное значение на основе установленных выше значений:
String msg = Whitebox.invokeMethod(obj, "privateMethodToBeTested", "param1");
Assert.assertEquals(privateMsg, msg);
Или же
Если частный метод classUnderTest не возвращает значение, но устанавливает другое частное поле, вы можете получить значение этого частного поля, чтобы увидеть, правильно ли оно установлено:
//To get value of private field
MyClass obj = Whitebox.getInternalState(classUnderTest, "foo");
assertThat(obj, is(notNull(MyClass.class))); // or test value
Я хотел бы внести свой вклад в то, что вы обеспокоены тем, что не тестируете частные методы, как предлагается во многих сообщениях, учтите, что инструмент покрытие кода определит точно, сколько вашего кода проверено и где он протекает, поэтому количественно приемлемо сделать это.
Я собираюсь сказать то, что нужно сказать, но многим, возможно, не понравится это слышать. Те из вас, кто дает ответы, которые направляют автора вопроса на «обходной путь», оказывают сообществу медвежью услугу. Тестирование - это основная часть всех инженерных дисциплин, вы бы не хотели покупать автомобиль, который не прошел должным образом и не прошел значимое тестирование, так зачем кому-то покупать или использовать плохо протестированное программное обеспечение? Причина, по которой люди все равно это делают, вероятно, заключается в том, что последствия плохо протестированного программного обеспечения ощущаются задолго до факта, и мы обычно не связываем их с телесными повреждениями. Это очень опасное восприятие, которое будет трудно изменить, но мы обязаны предоставлять безопасные продукты независимо от того, что даже руководство заставляет нас делать. Подумайте о взломе Equifax ...
Мы должны стремиться создать среду, которая поощряет хорошие практики разработки программного обеспечения, это не означает изгнание слабых / ленивых среди нас, которые не относятся к своему делу всерьез, а скорее, чтобы создать статус-кво подотчетности и саморефлексии, который воодушевил бы всех. стремиться к росту как мысленно, так и умело. Я все еще учусь и, возможно, сам ошибаюсь в восприятии / мнении, но я твердо верю, что мы должны нести ответственность за передовой опыт и избегать безответственных взломов или обходных путей решения проблем.
Вы делаете хорошие замечания (некоторые / большинство из которых уже были высказаны другими), но, пожалуйста, постарайтесь избежать враждебности, используя такие слова, как «псевдо разработчики / инженеры». Как вы сказали (и я согласен), мы не должны подвергать остракизму, а вместо этого создавать лучший статус-кво и помогать всем учиться и совершенствоваться.
@Constantinos Спасибо, что указали на это. Я вспоминаю, когда писал этот ответ, что был разочарован нехваткой ресурсов (или их восприятием) для изучения передового опыта. В то время едва окончив колледж, я искал критерий, который помог бы мне написать хороший код. Я согласен с тем, что язык, который я использовал, был неподходящим, и я отредактировал его. Спасибо.
Лучший и правильный законный способ протестировать частный метод Java из тестовой среды - это аннотация @VisibleForTesting над методом, поэтому тот же метод будет виден для тестовой среды, как и общедоступный метод.
В C++: перед включением заголовка класса с частной функцией, которую вы хотите протестировать
Используйте этот код:
#define private public
#define protected public
Ой. Я думаю, это работает нормально, но я бы предпочел использовать объявления друзей, а не возиться с #defines на таком базовом уровне.
Да, это сработает ... но, черт возьми, это (вероятно) ужасно для вашего дизайна
К вашему сведению, это больше не работает в VS2019, больше нельзя использовать макросы таким образом.
PowerMockito создан для этого. Использовать зависимость от maven
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
Тогда ты можешь сделать
import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);
Моя команда и я используем Typemock, у которого есть API, позволяющий поддельные непубличные методы. Недавно они добавили возможность поддельные невидимые типы и использовать XUnit.
Если у вас есть случай, когда вам действительно нужно напрямую протестировать частный метод / класс и т.д., вы можете использовать отражение, как уже упоминалось в других ответах. Однако если дело доходит до этого, вместо того, чтобы напрямую заниматься отражением, я бы предпочел использовать классы утилиты, предоставляемые вашей структурой. Например, для Java у нас есть:
ReflectionUtils Пружинный каркас
ReflectionUtils JUnit
О том, как их использовать, вы можете найти множество статей в Интернете. Вот один, который мне особенно понравился:
Вы можете создать специальный общедоступный метод, чтобы проксировать частный метод для тестирования. Аннотации @TestOnly из коробки доступны при использовании IntelliJ. Обратной стороной является то, что если кто-то хочет использовать частный метод в общедоступном контексте, он может это сделать. Но он будет предупрежден по аннотации и названию метода. В IntelliJ при этом появится предупреждение.
import org.jetbrains.annotations.TestOnly
class MyClass {
private void aPrivateMethod() {}
@TestOnly
public void aPrivateMethodForTest() {
aPrivateMethod()
}
}
Есть еще один подход к тестированию ваших частных методов. Если вы «включаете утверждение» в конфигурациях запуска, вы можете выполнить модульное тестирование своего метода внутри самого метода. Например;
assert ("Ercan".equals(person1.name));
assert (Person.count == 2);
PowerMock.Whitebox - лучший вариант, который я видел, но когда я читаю его исходный код, он читает частные поля с отражением, поэтому я думаю, что у меня есть свой ответ:
Через некоторое время, когда я пересмотрел это, я все еще верю, что это правда, но я увидел более подходящие подходы.
Во-первых, Powermock.Whitebox все еще можно использовать.
Кроме того, Mockito Whitebox был скрыт после v2 (последняя версия, которую я могу найти с Whitebox, - это testImplementation 'org.mockito:mockito-core:1.10.19'), и он всегда был частью пакета org.mockito.internal, который подвержен критическим изменениям в будущем (см. эта почта). Так что теперь я стараюсь не использовать его.
В проектах Gradle / Maven, если вы определяете частные методы или поля, нет других способов, кроме отражения, для получения доступа к ним, поэтому первая часть остается верной. Но, если вы измените видимость на «частный пакет», тесты, следующие той же структуре в пакете test, будут иметь к ним доступ.. Это еще одна важная причина, по которой нам рекомендуется создавать ту же иерархию в пакетах main и test. Итак, когда у вас есть контроль над производственным кодом, а также над тестами, удаление этого модификатора доступа private может быть лучшим вариантом для вас, потому что относительно он не оказывает большого влияния. И это делает возможным тестирование, а также шпионаж частным методом.
@Autowired
private SomeService service; // with a package private method "doSomething()"
@Test
void shouldReturnTrueDoSomething() {
assertThat(doSomething(input), is(true)); // package private method testing
}
@Test
void shouldReturnTrueWhenServiceThrowsException() {
SomeService spy = Mockito.spy(service); // spying real object
doThrow(new AppException()).when(spy).doSomething(input); // spy package private method
...
}
Что касается внутренних полей, в Spring у вас есть ReflectionUtils.setField().
Наконец, иногда мы можем обойти саму проблему: если необходимо выполнить требование покрытия, возможно, вы можете переместить эти частные методы во внутренний статический класс и игнорировать этот класс в Jacoco. Я просто нашел способ игнорировать внутренний класс в задачах Jacoco gradle. Другой вопрос
Android имеет аннотацию @VisibleForTesting из пакета android.support.annotation.
The
@VisibleForTestingannotation indicates that an annotated method is more visible than normally necessary to make the method testable. This annotation has an optionalotherwiseargument that lets you designate what the visibility of the method should have been if not for the need to make it visible for testing. Lint uses theotherwiseargument to enforce the intended visibility.
На практике это означает, что вы должны сделать метод открытым для тестирования, и в аннотации @VisibleForTesting будет отображаться предупреждение.
Например
package com.mypackage;
public class ClassA {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static void myMethod() {
}
}
И когда вы вызываете ClassA.myMethod () в том же пакете (com.mypackage), вы увидите предупреждение.
Для C++ (начиная с C++ 11) добавление тестового класса в качестве друга отлично работает и не нарушает производственную инкапсуляцию.
Предположим, у нас есть класс Foo с некоторыми частными функциями, которые действительно требуют тестирования, и класс FooTest, который должен иметь доступ к закрытым членам Foo. Затем мы должны написать следующее:
// prod.h: some production code header
// forward declaration is enough
// we should not include testing headers into production code
class FooTest;
class Foo
{
// that does not affect Foo's functionality
// but now we have access to Foo's members from FooTest
friend FooTest;
public:
Foo();
private:
bool veryComplicatedPrivateFuncThatReallyRequiresTesting();
}
// test.cpp: some test
#include <prod.h>
class FooTest
{
public:
void complicatedFisture() {
Foo foo;
ASSERT_TRUE(foo.veryComplicatedPrivateFuncThatReallyRequiresTesting());
}
}
int main(int /*argc*/, char* argv[])
{
FooTest test;
test.complicatedFixture(); // and it really works!
}
Существует также трюк с шаблоном, который позволяет получить доступ к закрытым членам (через косвенный указатель IIRC) или просто к -fno-access-control (который просто игнорирует ограничения private и protected).
Следующим Reflection TestUtil может быть используется в общем для проверки частные методы для их атомарности.
import com.google.common.base.Preconditions;
import org.springframework.test.util.ReflectionTestUtils;
/**
* <p>
* Invoker
* </p>
*
* @author
* @created Oct-10-2019
*/
public class Invoker {
private Object target;
private String methodName;
private Object[] arguments;
public <T> T invoke() {
try {
Preconditions.checkNotNull(target, "Target cannot be empty");
Preconditions.checkNotNull(methodName, "MethodName cannot be empty");
if (null == arguments) {
return ReflectionTestUtils.invokeMethod(target, methodName);
} else {
return ReflectionTestUtils.invokeMethod(target, methodName, arguments);
}
} catch (Exception e) {
throw e;
}
}
public Invoker withTarget(Object target) {
this.target = target;
return this;
}
public Invoker withMethod(String methodName) {
this.methodName = methodName;
return this;
}
public Invoker withArguments(Object... args) {
this.arguments = args;
return this;
}
}
Object privateMethodResponse = new Invoker()
.withTarget(targetObject)
.withMethod(PRIVATE_METHOD_NAME_TO_INVOKE)
.withArguments(arg1, arg2, arg3)
.invoke();
Assert.assertNotNutll(privateMethodResponse)
Если вы используете только Mockito:
Вы можете рассматривать частный метод как часть тестируемого общедоступного метода. Вы можете убедиться, что вы покрываете все случаи частным методом при тестировании общедоступного метода.
Предположим, вы являетесь пользователем только mockito (вам не разрешено или вы не хотите использовать Powermock, отражение или любые подобные инструменты), и вы не хотите изменять существующий код или тестируемые библиотеки, это может быть лучшим способом.
Единственное, что вам нужно сделать, если вы выберете этот способ, - это переменные (определяемые пользователем объекты), объявленные локально в частных методах. Если частный метод зависит от локально объявленных переменных объектов и их методов, убедитесь, что вы объявляете эти определенные пользователем объекты глобально как частный объект, а не локально объявленные объекты. Вы можете создать экземпляры этих объектов локально.
Это позволяет имитировать эти объекты и вводить их обратно в объект тестирования. Вы также можете издеваться над их методами (используя когда / тогда).
Это позволит вам протестировать частный метод без ошибок при тестировании общедоступного метода.
Преимущества 1. покрытие кода 2. Возможность протестировать полный частный метод.
Недостатки 1. Область действия объекта. Если вы не хотите, чтобы объект был открыт для других методов в том же классе, это может быть не ваш путь. 2. Возможно, вам придется тестировать частный метод несколько раз, когда он вызывается из разных открытых методов и / или в одном и том же методе несколько раз.
Привет, используйте этот служебный класс, если вы находитесь на весна.
ReflectionTestUtils.invokeMethod(new ClassName(), "privateMethodName");
В вашем классе:
namespace my_namespace {
#ifdef UNIT_TEST
class test_class;
#endif
class my_class {
public:
#ifdef UNIT_TEST
friend class test_class;
#endif
private:
void fun() { cout << "I am private" << endl; }
}
}
В вашем классе модульного теста:
#ifndef UNIT_TEST
#define UNIT_TEST
#endif
#include "my_class.h"
class my_namespace::test_class {
public:
void fun() { my_obj.fun(); }
private:
my_class my_obj;
}
void my_unit_test() {
test_class test_obj;
test_obj.fun(); // here you accessed the private function ;)
}
Детализируйте мой образец с ломбоком, как показано ниже. частное поле, частный метод:
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Student student = new Student();
Field privateFieldName = Student.class.getDeclaredField("name");
privateFieldName.setAccessible(true);
privateFieldName.set(student, "Naruto");
Field privateFieldAge = Student.class.getDeclaredField("age");
privateFieldAge.setAccessible(true);
privateFieldAge.set(student, "28");
System.out.println(student.toString());
Method privateMethodGetInfo = Student.class.getDeclaredMethod("getInfo", String.class, String.class);
privateMethodGetInfo.setAccessible(true);
System.out.println(privateMethodGetInfo.invoke(student, "Sasuke", "29"));
}
@Setter
@Getter
@ToString
class Student {
private String name;
private String age;
private String getInfo(String name, String age) {
return name + "-" + age;
}
}
Лучший способ протестировать частный метод - не тестировать его напрямую