Как мне проверить приватную функцию или класс, который имеет закрытые методы, поля или внутренние классы? - PullRequest
2484 голосов
/ 29 августа 2008

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

Кажется плохим изменить модификатор доступа для метода или функции просто для того, чтобы можно было запустить тест.

Ответы [ 48 ]

1532 голосов
/ 29 августа 2008

Обновление:

Примерно через 10 лет, пожалуй, лучший способ протестировать закрытый метод или любой недоступный элемент - через @Jailbreak из Коллектор framework.

@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;

Таким образом, ваш код остается безопасным и читабельным. Никаких компромиссов в дизайне, никаких переэкспонированных методов и полей для испытаний.

Если у вас есть какое-то устаревшее Java приложение, и вам не разрешено изменять видимость ваших методов, лучший способ протестировать частные методы - это использовать отражение,

Внутренне мы используем помощники для получения / установки переменных 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);

Примечания:
1. TargetClass.getDeclaredMethod(methodName, argClasses) позволяет взглянуть на private методы. То же самое относится и к getDeclaredField.
2. setAccessible(true) требуется для игры с рядовыми.

561 голосов
/ 29 августа 2008

Лучший способ протестировать закрытый метод - использовать другой публичный метод. Если это невозможно, то выполняется одно из следующих условий:

  1. Закрытый метод - мертвый код
  2. Рядом с классом, который вы тестируете, ощущается запаха дизайна
  3. Метод, который вы пытаетесь проверить, не должен быть закрытым
289 голосов
/ 09 сентября 2008

Когда у меня есть частные методы в классе, которые достаточно сложны, и я чувствую необходимость непосредственно тестировать частные методы, это пахнет кодом: мой класс слишком сложен.

Мой обычный подход к решению таких проблем состоит в том, чтобы выявить новый класс, который содержит интересные биты. Часто этот метод и поля, с которыми он взаимодействует, и, возможно, другой метод или два, могут быть извлечены в новый класс.

Новый класс представляет эти методы как «публичные», поэтому они доступны для модульного тестирования. Новые и старые классы теперь стали проще, чем исходный, что очень удобно для меня (мне нужно, чтобы все было просто, или я заблудился!).

Обратите внимание, что я не предлагаю людям создавать классы, не используя свой мозг! Суть в том, чтобы использовать силы модульного тестирования, чтобы помочь вам найти хорошие новые классы.

257 голосов
/ 25 июня 2009

Я использовал отражение , чтобы сделать это для Java в прошлом, и, на мой взгляд, это было большой ошибкой.

Строго говоря, вы должны , а не писать модульные тесты, которые напрямую тестируют частные методы. То, что вы должны проверить, является публичным контрактом, который класс имеет с другими объектами; Вы никогда не должны напрямую проверять внутренности объекта. Если другой разработчик хочет внести небольшое внутреннее изменение в класс, которое не влияет на публичный контракт классов, он / она должен изменить ваш тест на основе отражения, чтобы убедиться, что он работает. Если вы будете делать это неоднократно в течение всего проекта, модульные тесты перестанут быть полезным измерением работоспособности кода и станут препятствием для разработки и раздражением для команды разработчиков.

Вместо этого я рекомендую использовать инструмент покрытия кода, такой как Cobertura, чтобы гарантировать, что написанные вами модульные тесты обеспечивают достойный охват кода в частных методах. Таким образом, вы косвенно проверяете, что делают частные методы, и поддерживаете более высокий уровень гибкости.

198 голосов
/ 29 августа 2008

Из этой статьи: Тестирование приватных методов с помощью JUnit и SuiteRunner (Билл Веннерс), у вас есть 4 варианта:

  • Не тестировать приватные методы.
  • Предоставить доступ к пакету методов.
  • Использовать вложенный тестовый класс.
  • Используйте отражение.
111 голосов
/ 29 августа 2008

Обычно юнит-тест предназначен для использования открытого интерфейса класса или юнита. Следовательно, частные методы - это детали реализации, которые вы не ожидаете явно протестировать.

62 голосов
/ 05 июля 2010

Всего два примера, где я хотел бы проверить приватный метод:

  1. Процедуры расшифровки - Я бы не стал хочу сделать их видимыми для всех, чтобы увидеть только для ради тестирования, иначе кто-нибудь может использовать их для расшифровки. Но они свойственный коду, сложный, и необходимость всегда работать (очевидное исключение - отражение, которое можно использовать для просмотра даже частных методов в большинстве случаев, когда SecurityManager не настроен для предотвращения этого).
  2. Создание SDK для сообщества потребление. Здесь публика берет на себя совершенно другое значение, так как это это код, который может увидеть весь мир (не только внутренний для моего приложения). я кладу код в частные методы, если я не хочу, чтобы пользователи SDK увидели это - я не вижу это как запах кода, просто как работает программирование SDK. Но из Конечно, мне все еще нужно проверить мой частные методы, и они где функциональность моего SDK на самом деле живет.

Я понимаю идею только проверки "контракта". Но я не вижу, чтобы кто-то мог выступать за то, чтобы на самом деле не тестировать код - ваш пробег может отличаться.

Так что мой компромисс заключается в том, чтобы усложнить JUnits отражением, а не поставить под угрозу мою безопасность и SDK.

54 голосов
/ 29 августа 2008

Закрытые методы вызываются публичным методом, поэтому входные данные ваших открытых методов также должны тестировать закрытые методы, которые вызываются этими открытыми методами. В случае сбоя открытого метода это может быть сбой в частном методе.

34 голосов
/ 20 марта 2016

Другой подход, который я использовал, - это изменить частный метод для упаковки частного или защищенного, а затем дополнить его аннотацией @ VisibleForTesting библиотеки Google Guava.

Это скажет любому, кто использует этот метод, проявлять осторожность и не обращаться к нему напрямую даже в пакете. Также тестовый класс не обязательно должен находиться в том же пакете физически , но в том же пакете в папке test .

Например, если тестируемый метод находится в src/main/java/mypackage/MyClass.java, тогда ваш тестовый вызов должен быть помещен в src/test/java/mypackage/MyClassTest.java. Таким образом, вы получили доступ к методу тестирования в своем классе тестирования.

32 голосов
/ 12 января 2011

Для тестирования устаревшего кода с большими и причудливыми классами часто очень полезно иметь возможность протестировать один приватный (или публичный) метод, который я пишу прямо сейчас .

Я использую 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...