Как выполнить модульное тестирование (с использованием xUnit) класса, который имеет внутренние закрытые методы, поля или вложенные классы? Или функция, которая делается частной благодаря наличию внутренней связи ( static
в C / C ++) или находится в закрытом ( анонимном ) пространстве имен?
Кажется плохим изменить модификатор доступа для метода или функции просто для того, чтобы можно было запустить тест.
java
unit-testing
tdd
Raedwald
источник
источник
Ответы:
Если у вас есть какое-то устаревшее Java- приложение, и вы не можете изменять видимость ваших методов, лучший способ протестировать частные методы - это использовать отражение .
Внутренне мы используем помощники для получения / установки
private
иprivate static
переменных, а также для вызоваprivate
иprivate static
методов. Следующие шаблоны позволят вам делать практически все, что связано с закрытыми методами и полями. Конечно, вы не можете изменятьprivate static final
переменные посредством отражения.И для полей:
источник
Лучший способ проверить приватный метод - использовать другой публичный метод. Если это невозможно сделать, то выполняется одно из следующих условий:
источник
Когда у меня есть закрытые методы в классе, которые достаточно сложны, и я чувствую необходимость непосредственно тестировать закрытые методы, это - запах кода: мой класс слишком сложен.
Мой обычный подход к решению таких проблем - выявить новый класс, содержащий интересные биты. Часто этот метод и поля, с которыми он взаимодействует, и, возможно, другой метод или два, могут быть извлечены в новый класс.
Новый класс представляет эти методы как «публичные», поэтому они доступны для модульного тестирования. Новые и старые классы теперь проще, чем исходный, что очень удобно для меня (мне нужно, чтобы все было просто, или я заблудился!).
Обратите внимание, что я не предлагаю людям создавать классы, не используя свой мозг! Суть в том, чтобы использовать силы модульного тестирования, чтобы помочь вам найти хорошие новые классы.
источник
В прошлом я использовал рефлексию, чтобы сделать это для Java, и, на мой взгляд, это было большой ошибкой.
Строго говоря, вы не должны писать модульные тесты, которые напрямую тестируют частные методы. То, что вы должны тестировать, это публичный контракт, который класс имеет с другими объектами; Вы никогда не должны напрямую проверять внутренности объекта. Если другой разработчик хочет внести небольшое внутреннее изменение в класс, которое не влияет на публичный контракт классов, он / она должен изменить ваш тест на основе отражения, чтобы убедиться, что он работает. Если вы будете делать это многократно в течение всего проекта, модульные тесты перестанут быть полезным измерением работоспособности кода и станут препятствием для разработки и раздражением для команды разработчиков.
Вместо этого я рекомендую использовать инструмент покрытия кода, такой как Cobertura, чтобы гарантировать, что написанные вами модульные тесты обеспечивают достойное покрытие кода в частных методах. Таким образом, вы косвенно проверяете, что делают частные методы, и поддерживаете более высокий уровень гибкости.
источник
Из этой статьи: Тестирование частных методов с помощью JUnit и SuiteRunner (Билл Веннерс), у вас в основном есть 4 варианта:
источник
Обычно модульный тест предназначен для использования открытого интерфейса класса или модуля. Следовательно, частные методы - это детали реализации, которые вы не ожидаете явно протестировать.
источник
Всего два примера, где я хотел бы протестировать приватный метод:
SecurityManager
не настроено это предотвращать).Я понимаю идею только тестирования "контракта". Но я не вижу, чтобы кто-то мог выступать за то, чтобы на самом деле не тестировать код - ваш пробег может отличаться.
Поэтому мой компромисс заключается в том, чтобы усложнить JUnits отражением, а не поставить под угрозу мою безопасность и SDK.
источник
private
это не единственная альтернатива. Отсутствие модификатора доступаpackage private
означает, что вы можете выполнить его модульное тестирование, пока ваш модульный тест находится в одном пакете.Закрытые методы вызываются публичным методом, поэтому входные данные ваших открытых методов также должны проверять закрытые методы, которые вызываются этими открытыми методами. Когда публичный метод дает сбой, это может быть сбой в частном методе.
источник
Другой подход, который я использовал, - это изменить приватный метод для упаковки приватного или защищенного, а затем дополнить его аннотацией @VisibleForTesting библиотеки Google Guava.
Это скажет любому, кто использует этот метод, быть осторожным и не иметь к нему прямого доступа даже в пакете. Кроме того, тестовый класс не обязательно должен находиться в одном и том же пакете физически , но в том же пакете в папке с тестами .
Например, если тестируемый метод находится в,
src/main/java/mypackage/MyClass.java
то ваш тестовый вызов должен быть помещен вsrc/test/java/mypackage/MyClassTest.java
. Таким образом, вы получили доступ к тестовому методу в вашем тестовом классе.источник
Для тестирования унаследованного кода с большими и причудливыми классами часто очень полезно иметь возможность протестировать один приватный (или публичный) метод, который я пишу прямо сейчас .
Я использую пакет junitx.util.PrivateAccessor для Java. Много полезных однострочников для доступа к приватным методам и приватным полям.
источник
Class
качестве первого параметра в этих методах, что вы обращаетесь только кstatic
членам.В Spring Framework вы можете протестировать закрытые методы, используя этот метод:
Например:
источник
Попробовав решение Cem Catikkas с использованием отражения для Java, я бы сказал, что это было более элегантное решение, чем я описал здесь. Однако, если вы ищете альтернативу использованию отражения и имеете доступ к тестируемому источнику, это все равно будет вариант.
Возможно, есть преимущество в тестировании частных методов класса, особенно при разработке на основе тестов , когда вы хотели бы создавать небольшие тесты, прежде чем писать какой-либо код.
Создание теста с доступом к закрытым членам и методам может проверить области кода, на которые сложно ориентироваться, имея доступ только к открытым методам. Если открытый метод включает несколько этапов, он может состоять из нескольких частных методов, которые затем можно протестировать индивидуально.
Преимущества:
Недостатки:
Однако, если для непрерывного тестирования требуется этот метод, это может быть сигналом того, что частные методы должны быть извлечены, что может быть проверено традиционным, общедоступным способом.
Вот замысловатый пример того, как это будет работать:
Внутренний класс будет скомпилирован в
ClassToTest$StaticInnerTest
.См. Также: Java Совет 106: статические внутренние классы для удовольствия и выгоды
источник
Как уже говорили другие ... не тестируйте частные методы напрямую. Вот несколько мыслей:
Запустите покрытие кода на модульных тестах. Если вы видите, что методы не полностью протестированы, добавьте тесты, чтобы увеличить охват. Стремитесь к 100% охвату кода, но понимайте, что вы, вероятно, не получите его.
источник
Частные методы потребляются публичными. В противном случае это мертвый код. Вот почему вы тестируете открытый метод, утверждая ожидаемые результаты открытого метода и, следовательно, частные методы, которые он использует.
Тестирование частных методов должно быть проверено путем отладки перед запуском ваших модульных тестов для открытых методов.
Они также могут быть отлажены с помощью управляемой тестами разработки, отлаживая ваши юнит-тесты, пока все ваши утверждения не будут выполнены.
Я лично считаю, что лучше создавать классы с использованием TDD; создание открытых заглушек метода, а затем генерация модульных тестов со всеми заранее заданными утверждениями, поэтому ожидаемый результат метода определяется до его кодирования. Таким образом, вы не идете по неверному пути, чтобы утверждения модульного теста соответствовали результатам. Ваш класс будет устойчивым и соответствует требованиям, когда пройдут все ваши юнит-тесты.
источник
При использовании Spring ReflectionTestUtils предоставляет несколько удобных инструментов, которые помогут здесь с минимальными усилиями. Например, чтобы настроить макет на приватном члене без необходимости добавлять нежелательный общедоступный установщик:
источник
Если вы пытаетесь протестировать существующий код, который вы неохотно или не можете изменить, рефлексия - хороший выбор.
Если дизайн класса по-прежнему гибкий, и у вас есть сложный закрытый метод, который вы хотите протестировать отдельно, я предлагаю вам выделить его в отдельный класс и протестировать этот класс отдельно. Это не должно изменять публичный интерфейс исходного класса; он может внутренне создать экземпляр вспомогательного класса и вызвать вспомогательный метод.
Если вы хотите проверить сложные условия ошибки, возникающие из вспомогательного метода, вы можете пойти дальше. Извлеките интерфейс из вспомогательного класса, добавьте общедоступный метод получения и установки в исходный класс, чтобы внедрить вспомогательный класс (используемый через его интерфейс), а затем внедрите фиктивную версию вспомогательного класса в исходный класс, чтобы проверить, как оригинальный класс реагирует на исключения из помощника. Этот подход также полезен, если вы хотите протестировать исходный класс без тестирования вспомогательного класса.
источник
Тестирование приватных методов нарушает инкапсуляцию вашего класса, потому что каждый раз, когда вы изменяете внутреннюю реализацию, вы нарушаете клиентский код (в данном случае, тесты).
Так что не проверяйте частные методы.
источник
Ответ со страницы часто задаваемых вопросов JUnit.org :
PS Я основной участник Dp4j , спросите , нужна ли вам помощь. :)
источник
Если вы хотите протестировать приватные методы унаследованного приложения, в котором вы не можете изменить код, одним из вариантов для Java является jMockit , который позволит вам создавать макеты для объекта, даже когда они являются закрытыми для класса.
источник
Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Я склонен не проверять частные методы. Там лежит безумие. Лично я считаю, что вам следует тестировать только открытые интерфейсы (включая защищенные и внутренние методы).
источник
Если вы используете JUnit, взгляните на junit-addons . Он имеет возможность игнорировать модель безопасности Java и получать доступ к закрытым методам и атрибутам.
источник
Я бы посоветовал вам немного изменить свой код. Когда вам нужно начать думать об использовании рефлексии или чего-то другого для простого тестирования кода, что-то идет не так с вашим кодом.
Вы упомянули разные типы проблем. Давайте начнем с частных полей. В случае приватных полей я бы добавил новый конструктор и вставил в него поля. Вместо этого:
Я бы использовал это:
Это не будет проблемой даже с некоторым устаревшим кодом. Старый код будет использовать пустой конструктор, и, если вы спросите меня, рефакторинговый код будет выглядеть чище, и вы сможете ввести необходимые значения в тест без отражения.
Теперь о частных методах. По моему личному опыту, когда вам нужно заглушить приватный метод для тестирования, тогда этот метод не имеет ничего общего с этим классом. В этом случае обычным шаблоном будет заключаться в том, чтобы обернуть его в интерфейсе, как,
Callable
а затем передать этот интерфейс также в конструкторе (с помощью этого трюка с несколькими конструкторами):В основном все, что я написал, выглядит как шаблон внедрения зависимостей. По моему личному опыту, это действительно полезно при тестировании, и я думаю, что этот вид кода чище и его будет легче поддерживать. Я бы сказал то же самое о вложенных классах. Если вложенный класс содержит тяжелую логику, было бы лучше, если бы вы переместили его как закрытый класс пакета и внедрили его в класс, который в этом нуждается.
Есть также несколько других шаблонов проектирования, которые я использовал при рефакторинге и обслуживании старого кода, но все зависит от случаев тестирования вашего кода. Использование рефлексии по большей части не является проблемой, но когда у вас есть корпоративное приложение, которое тщательно тестируется и тесты запускаются перед каждым развертыванием, все становится очень медленным (это просто раздражает, и мне не нравятся подобные вещи).
Существует также сеттерная инъекция, но я бы не рекомендовал ее использовать. Я лучше придерживаюсь конструктора и инициализирую все, когда это действительно необходимо, оставляя возможность для внедрения необходимых зависимостей.
источник
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 легче поддерживать. Есть случаи, когда инъекции, подобные этому, имеют смысл, но я не вижу в вашем примере «легче поддерживать»Закрытый метод доступен только в том же классе. Таким образом, нет способа протестировать «закрытый» метод целевого класса из любого тестового класса. Выход в том, что вы можете выполнить модульное тестирование вручную или изменить свой метод с «частного» на «защищенный».
И тогда защищенный метод может быть доступен только в том же пакете, где определен класс. Итак, тестирование защищенного метода целевого класса означает, что мы должны определить ваш тестовый класс в том же пакете, что и целевой класс.
Если все вышеперечисленное не соответствует вашим требованиям, используйте способ отражения для доступа к приватному методу.
источник
Как указывалось выше, хороший способ - проверить их через общедоступные интерфейсы.
Если вы сделаете это, будет полезно использовать инструмент покрытия кода (например, Emma), чтобы увидеть, действительно ли ваши частные методы выполняются из ваших тестов.
источник
Вот моя общая функция для проверки приватных полей:
источник
Пожалуйста, смотрите ниже для примера;
Необходимо добавить следующую инструкцию импорта:
Теперь вы можете напрямую передать объект, у которого есть закрытый метод, имя метода, который нужно вызвать, и дополнительные параметры, как показано ниже.
источник
Сегодня я добавил библиотеку Java, чтобы помочь в тестировании частных методов и полей. Он был разработан с учетом Android, но он действительно может быть использован для любого проекта Java.
Если вы получили некоторый код с закрытыми методами, полями или конструкторами, вы можете использовать BoundBox . Это именно то, что вы ищете. Ниже приведен пример теста, который обращается к двум частным полям действия Android для его проверки:
BoundBox позволяет легко тестировать закрытые / защищенные поля, методы и конструкторы. Вы даже можете получить доступ к вещам, которые скрыты по наследству. Действительно, BoundBox нарушает инкапсуляцию. Это даст вам доступ ко всему этому через рефлексию, НО все проверяется во время компиляции.
Это идеальное решение для тестирования устаревшего кода. Используйте это осторожно. ;)
https://github.com/stephanenicolas/boundbox
источник
Во-первых, я выброшу этот вопрос: зачем вашим частным членам нужно изолированное тестирование? Являются ли они такими сложными, обеспечивающими такое сложное поведение, которое требует тестирования вне публичной среды? Это модульное тестирование, а не тестирование по строке кода. Не парься по мелочам.
Если они настолько велики, достаточно велики, что каждый из этих закрытых членов по своей сложности является единым целым, рассмотрите возможность рефакторинга таких закрытых членов из этого класса.
Если рефакторинг неуместен или неосуществим, можете ли вы использовать шаблон стратегии для замены доступа к этим закрытым функциям / классам-членам, когда они проходят модульное тестирование? При модульном тестировании стратегия обеспечит дополнительную проверку, но в сборках релизов это будет простой проход.
источник
Недавно я столкнулся с этой проблемой и написал небольшой инструмент под названием Picklock , который позволяет избежать проблем явного использования API отражения Java, два примера:
Вызов методов, например
private void method(String s)
- с помощью отражения JavaВызов методов, например
private void method(String s)
- ПиклокУстановка полей, например
private BigInteger amount;
- с помощью отражения JavaНастройка полей, например
private BigInteger amount;
- Пиклокисточник
PowerMockito создан для этого. Используйте maven зависимость
Тогда вы можете сделать
источник