Как мне проверить приватную функцию или класс, который имеет закрытые методы, поля или внутренние классы?

2407

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

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

  • 213
    Лучший способ проверить приватный метод - не тестировать его напрямую.
  • 25
    Посмотрите статью Тестирование частных методов с помощью JUnit и SuiteRunner .
Показать ещё 19 комментариев
Теги:
unit-testing
junit
tdd

46 ответов

1408
Лучший ответ

Если у вас есть какое-то устаревшее 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) требуется для игры с рядовыми.

  • 311
    Полезно, если вы, возможно, не знаете API, но если вам приходится тестировать закрытые методы таким образом, то с вашим дизайном что-то не так. Как говорит другой автор, модульное тестирование должно проверять контракт класса: если контракт слишком широкий и создает слишком большую часть системы, тогда следует рассмотреть проект.
  • 20
    Очень полезно. Используя это, важно иметь в виду, что он потерпит неудачу, если тесты будут выполняться после запутывания.
Показать ещё 23 комментария
567

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

  • Частный метод - это мертвый код
  • У класса, который вы тестируете, есть запах дизайна.
  • Метод, который вы пытаетесь проверить, не должен быть закрытым
  • 972
    Не согласен. Вполне допустимо иметь алгоритм в частном методе, который требует больше модульного тестирования, чем практично через общедоступные интерфейсы класса.
  • 59
    Привет, мистер Шини Да, но тогда у вас будут хрупкие испытания. Также см. Номер 2 в моем ответе.
Показать ещё 20 комментариев
281

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

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

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

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

  • 18
    Вопрос был в том, как протестировать приватные методы. Вы говорите, что создадите новый класс для этого (и добавите намного больше сложности), и после предложите не создавать новый класс. Так как же проверить приватные методы?
  • 4
    Я думаю, что если у вас есть метод, нет необходимости создавать другой класс, чтобы иметь возможность протестировать этот метод. Я не думаю, что дизайн класса был здесь вопросом. Поэтому я предполагаю, что автор сделал все, чтобы дизайн был правильным, поэтому введение другого класса для одного метода просто увеличивает сложность. Конечно, когда у вас достаточно сложная программа, явных ошибок не будет ..
Показать ещё 12 комментариев
255

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

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

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

  • 67
    +1 к этому. На мой взгляд, это лучший ответ на вопрос. Тестируя частные методы, вы тестируете реализацию. Это противоречит цели модульного тестирования, которое заключается в проверке входных / выходных данных классового контракта. Тест должен знать только о реализации, чтобы смоделировать методы, которые он вызывает для своих зависимостей. Ничего более. Если вы не можете изменить свою реализацию без необходимости менять тест, скорее всего, ваша стратегия тестирования плохая.
  • 8
    @Colin M Это не совсем то, о чем он спрашивает;) Пусть он сам решит, вы не знаете проект.
Показать ещё 1 комментарий
181

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

  • Не проверяйте частные методы.
  • Предоставьте доступ к пакетам методов.
  • Использовать вложенный тестовый класс.
  • Используйте отражение.
  • 47
    @ user86614 Ввод тестового кода в производственный код не очень хорошая идея.
  • 0
    @JimmyT., Зависит от того, для кого предназначен «производственный код». Я бы назвал код, созданный для приложений, размещенных в VPN, целевые пользователи которых являются системными администраторами, рабочим кодом.
Показать ещё 5 комментариев
97

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

  • 14
    Это лучший ответ IMO, или, как обычно говорят, тестовое поведение, а не методы. Модульное тестирование не является заменой метрик исходного кода, инструментов статического анализа кода и обзоров кода. Если частные методы настолько сложны, что им нужно разделить тесты, то, вероятно, их необходимо реорганизовать, а не создавать больше тестов.
  • 12
    так что не пишите приватные методы, просто создайте 10 других маленьких классов?
55

Только два примера того, где я хочу протестировать частный метод:

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

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

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

  • 3
    В то время как вы никогда не должны делать метод общедоступным только для того, чтобы протестировать его, private не единственная альтернатива Никакой модификатор доступа не является package private и означает, что вы можете выполнить его модульное тестирование, пока ваш модульный тест находится в одном пакете.
  • 0
    @ngreen, это все правда, но не в этом вопрос. ОП спрашивает о тестировании частных методов. По умолчанию не совпадает с частным; как уже говорилось, вы можете легко увидеть метод доступа по умолчанию из класса, просто объявив этот класс в том же пакете. При частном доступе вам требуется рефлексия, а это совершенно другая игра в мяч.
Показать ещё 3 комментария
50

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

  • 0
    Истинный факт Проблема в том, что реализация более сложная, например, если один открытый метод вызывает несколько частных методов. Какой из них потерпел неудачу? Или, если это сложный закрытый метод, который нельзя раскрыть или перенести в новый класс
  • 0
    Вы уже упоминали, почему закрытый метод должен быть протестирован. Вы сказали «тогда это может быть», поэтому вы не уверены. ИМО, следует ли тестировать метод, ортогонально его уровню доступа.
31

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

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

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

  • 1
    Я не знал об этом, это интересно, я все еще думаю, что если вам нужны такие аннотации, у вас есть проблемы с дизайном.
  • 2
    Для меня это все равно, что сказать, что вместо того, чтобы давать одноразовый ключ своему пожарному надзирателю для проверки пожарной тренировки, вы оставляете входную дверь незапертой со знаком на двери с надписью - «разблокирован для проверки - если вы не из наша компания, пожалуйста, не входите ".
29

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

Я использую 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);
  • 2
    Обязательно загрузите весь пакет JUnit-addons ( sourceforge.net/projects/junit-addons ), а не проект SourceAgeed, рекомендованный Forge PrivateAccessor.
  • 2
    И убедитесь, что когда вы используете Class качестве первого параметра в этих методах, вы получаете доступ только к static элементам.
24

Попробовав решение Cem Catikkas используя отражение для Java, я бы сказал, что его было более элегантным решением, чем я описал здесь. Однако, если вы ищете альтернативу использованию отражения и имеете доступ к источнику, который вы тестируете, это все равно будет вариантом.

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

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

Преимущества:

  • Можно проверить более тонкую детализацию

Недостатки:

  • Тестовый код должен находиться в том же файл как исходный код, который может быть более сложно поддерживать
  • Аналогично выходным файлам .class они должны оставаться в том же пакете, который указан в исходном коде

Однако, если для непрерывного тестирования требуется этот метод, это может быть сигнал о том, что частные методы должны быть извлечены, которые могут быть протестированы традиционным, общедоступным способом.

Вот скрупулезный пример того, как это будет работать:

// 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: Статические внутренние классы для удовольствия и прибыли

22

Как говорили другие... не проверяйте частные методы напрямую. Вот несколько мыслей:

  • Сохраняйте все методы небольшими и сфокусированными (легко проверить, легко найти, что не так)
  • Используйте инструменты покрытия кода. Мне нравится Cobertura (о счастливый день, похоже, что новая версия отсутствует!)

Запустите покрытие кода на модульных тестах. Если вы видите, что методы не полностью протестированы, добавьте тесты для получения покрытия. Стремитесь к 100% охвату кода, но поймите, что вы, вероятно, не получите его.

  • 0
    Вверх для покрытия кода. Неважно, что за логика в приватном методе, вы все равно вызываете эту логику через публичный метод. Инструмент покрытия кода может показать вам, какие части покрываются тестом, поэтому вы можете увидеть, тестировался ли ваш частный метод.
  • 0
    Я видел классы, где единственный публичный метод - это main [], и у них всплывающий графический интерфейс плюс подключение базы данных и нескольких веб-серверов по всему миру. Легко сказать "не проверяй косвенно" ...
20

Если использовать Spring, ReflectionTestUtils предоставляет некоторые удобные инструменты, которые помогают здесь с минимальными усилиями. Например, чтобы настроить макет на частном члене, не будучи вынужденным добавить нежелательный публичный сеттер:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);
20

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

Тестирование частных методов должно быть проверено путем отладки перед запуском ваших модульных тестов в общедоступных методах.

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

Я лично считаю, что лучше создавать классы с использованием TDD; создавая публичные куски методов, затем генерируя единичные тесты с помощью all утверждений, определенных заранее, поэтому ожидаемый результат метода определяется до того, как вы его закодируете. Таким образом, вы не ошибетесь, чтобы утверждения unit test соответствовали результатам. Затем ваш класс прочен и соответствует требованиям, когда проходят все тесты вашего устройства.

  • 2
    Хотя это и правда, это может привести к некоторым очень сложным тестам - лучше тестировать один метод за раз (модульное тестирование), а не целую группу из них. Не страшное предложение, но я думаю, что здесь есть лучшие ответы - это должно быть последним средством.
19

В Spring Framework вы можете протестировать закрытые методы, используя этот метод:

ReflectionTestUtils.invokeMethod()

Например:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");
19

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

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

Если вы хотите проверить сложные условия ошибки, исходящие из вспомогательного метода, вы можете пойти дальше. Извлеките интерфейс из вспомогательного класса, добавьте публичный getter и setter в исходный класс, чтобы ввести класс-помощник (используемый через его интерфейс), а затем введите макетную версию вспомогательного класса в исходный класс, чтобы проверить, как исходный класс отвечает на исключения из помощника. Этот подход также полезен, если вы хотите протестировать исходный класс, не тестируя также вспомогательный класс.

15

Тестирование частных методов ломает инкапсуляцию вашего класса, потому что каждый раз, когда вы меняете внутреннюю реализацию, вы нарушаете клиентский код (в данном случае, тесты).

Поэтому не проверяйте частные методы.

  • 4
    модульный тест и код src являются парой. Если вы измените src, возможно, вам придется сменить юнит-тест. В этом смысл теста джунит. Они должны гарантировать, что все работает как прежде. и хорошо, если они сломаются, если вы измените код.
  • 1
    Не соглашайтесь, что модульное тестирование должно измениться, если изменяется код Если вам приказано добавить функциональность в класс без изменения существующей функциональности класса, то существующие модульные тесты должны пройти даже после того, как вы изменили свой код. Как упоминает Питер, модульные тесты должны проверять интерфейс, а не то, как это делается внутри. В Test Driven Development модульные тесты создаются перед написанием кода, чтобы сосредоточиться на интерфейсе класса, а не на том, как он решается внутри.
14

Ответ от Страница часто задаваемых вопросов JUnit.org:

Но если вам нужно...

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

Если вы используете JDK 1.6 или выше, и вы комментируете свои тесты с помощью @Test, вы можете использовать Dp4j для отражения в ваших методах тестирования. Для подробности о том, как его использовать, см. этот тест script.

P.S. Я главный вклад в Dp4j, спросите меня, если вам нужно Помогите.:)

14

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

  • 4
    Как часть библиотеки jmockit у вас есть доступ к классу Deencapsulation, который упрощает тестирование приватных методов: Deencapsulation.invoke(instance, "privateMethod", param1, param2);
  • 0
    Я использую этот подход все время. Очень полезно
13

Если вы используете JUnit, посмотрите junit-addons. Он имеет возможность игнорировать модель безопасности Java и получать доступ к приватным методам и атрибутам.

12

Я стараюсь не тестировать частные методы. Там царит безумие. Лично я считаю, что вы должны тестировать только открытые интерфейсы (и которые включают защищенные и внутренние методы).

11

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

Вы упомянули о разных типах проблем. Давайте начнем с частных полей. В случае частных полей я бы добавил новый конструктор и ввел в него поля. Вместо этого:

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;
}

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

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

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

  • 1
    Не согласен с ClassToTest(Callable) . Это делает ClassToTest более сложным. Будь проще. Кроме того, для этого требуется, чтобы кто-то еще сказал ClassToTest что-то, ClassToTest должен руководить ClassToTest . Есть место для введения логики, но это не так. Вы только что сделали класс труднее поддерживать, а не легче.
  • 0
    Кроме того, предпочтительно, если ваш метод тестирования X не увеличивает сложность X до такой степени, что это может вызвать больше проблем ... и, следовательно, требует дополнительного тестирования ... что, если вы реализовали таким образом, что может вызвать больше проблем ... (это не бесконечные циклы; каждая итерация, вероятно, меньше, чем предыдущая, но все же раздражает)
Показать ещё 7 комментариев
11

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

И тогда защищенный метод может быть доступен только в том же пакете, где определен класс. Таким образом, тестирование защищенного метода целевого класса означает, что нам нужно определить ваш тестовый класс в том же пакете, что и целевой класс.

Если все вышеизложенное не соответствует вашему требованию, используйте способ отражения для доступа к приватному методу.

  • 0
    Вы смешиваете «защищенный» с «дружественным». Доступ к защищенному методу может получить только класс, чьи объекты могут быть назначены целевому классу (т. Е. Подклассам).
  • 1
    На самом деле в Java нет слова «Friendly», термин «пакет» означает отсутствие модификатора private / public / protected. Я бы только исправил этот ответ, но есть действительно хороший, который уже говорит об этом - поэтому я бы просто рекомендовал удалить его.
Показать ещё 1 комментарий
10

Вот моя общая функция для проверки частных полей:

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);
}
10

Сегодня я нажал библиотеку 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

  • 1
    Только что попробовал, BoundBox это простое и элегантное решение!
10

Как было сказано выше, хороший способ - проверить их через ваши общедоступные интерфейсы.

Если вы это сделаете, рекомендуется использовать инструмент покрытия кода (например, Эмма), чтобы проверить, действительно ли ваши частные методы выполняются из ваших тестов.

  • 0
    Вы не должны косвенно проверять! Не только прикосновение через освещение; проверить, что ожидаемый результат достигнут!
9

См. ниже пример:

Необходимо добавить следующий оператор импорта:

import org.powermock.reflect.Whitebox;

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

Whitebox.invokeMethod(obj, "privateMethod", "param1");
8

Недавно я столкнулся с этой проблемой и написал небольшой инструмент под названием Picklock, который позволяет избежать проблем с явным использованием 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) - by 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; - by Picklock

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));
8

Во-первых, я вышлю этот вопрос: почему ваши частные члены нуждаются в изолированном тестировании? Являются ли они такими сложными, обеспечивая такое сложное поведение, чтобы требовать тестирования отдельно от публичной поверхности? Это модульное тестирование, а не тестирование "линии-кода". Не потейте мелкие вещи.

Если они такие большие, достаточно большие, чтобы эти частные члены были "сложной" единицей, рассмотрите возможность реорганизации таких частных членов из этого класса.

Если рефакторинг является неприемлемым или неосуществимым, можете ли вы использовать шаблон стратегии для замены доступа к этим частным функциям-членам/классам участников в unit test? В unit test стратегия обеспечит дополнительную валидацию, но в релиз-сборках это будет простой переход.

  • 3
    Потому что часто конкретный фрагмент кода из открытого метода преобразуется во внутренний закрытый метод и на самом деле является критическим фрагментом логики, который вы, возможно, ошиблись. Вы хотите проверить это независимо от публичного метода
  • 0
    Даже самый короткий код иногда без модульного теста не является правильным. Просто попробуйте рассчитать разницу между двумя географическими углами. 4 строки кода, и большинство не будет делать это правильно с первой попытки. Такие методы нуждаются в модульном тестировании, потому что они составляют основу надежного кода. (Кроме того, такой полезный код также может быть публичным; менее защищенный
6

PowerMockito создан для этого. Использовать зависимость maven

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-mockito-release-full</artifactId>
    <version>1.6.4</version>
</dependency>

Затем вы можете сделать

import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);
6

Для Java я бы использовал отражение, так как мне не нравится идея изменить доступ к пакету по объявленному методу просто для тестирования. Тем не менее, я обычно просто проверяю общедоступные методы, которые также должны гарантировать, что частные методы работают правильно.

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

Это неверно. Вы, безусловно, можете, как упоминалось в ответить Cem Catikkas.

  • 0
    Вы не можете использовать отражение, чтобы получить приватные методы извне класса владельца, модификатор private также влияет на рефлексию. Вот хорошая статья на тему вопроса.
  • 0
    @jmfsg В статье, на которую вы ссылаетесь, специально говорится: «Тестирование частных методов немного сложнее, но мы все равно можем сделать это с помощью System.Reflection ». (Очевидно, вам нужен ReflectionPermission , но обычно это не проблема.)
4

В С++: перед включением заголовка класса, который имеет частную функцию, которую вы хотите проверить,

Используйте этот код:

#define private public
#define protected public
  • 0
    Кто-нибудь может подтвердить, что это хорошая практика?
  • 1
    Я использую это и работал очень хорошо
Показать ещё 3 комментария
3

Быстрое добавление комментария @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());
    }
 }
3

Я не уверен, является ли это хорошей техникой, но я разработал следующий шаблон для unit test частных методов:

Я не изменяю видимость метода, который я хочу проверить, и добавляю дополнительный метод. Вместо этого я добавляю дополнительный публичный метод для каждого частного метода, который я хочу проверить. Я вызываю этот дополнительный метод Test-Port и обозначаю их префиксом t_. Этот метод Test-Port затем просто обращается к соответствующему частному методу.

Кроме того, я добавляю флаг boolean в метод Test-Port, чтобы решить, предоставляю ли я доступ к приватному методу с помощью метода Test-Port извне. Этот флаг затем устанавливается глобально в статическом классе, где я размещаю, например. другие глобальные настройки для приложения. Поэтому я могу включить или отключить доступ к частным методам в одном месте, например. в соответствующем unit test.

3

Вы можете отключить ограничения доступа Java для отражения, чтобы частные ничего не делали.

Вызов setAccessible(true) делает это.

Единственное ограничение заключается в том, что ClassLoader может запретить вам это делать.

См. Подчинение защиты доступа Java для модульного тестирования (Росс Бертон) для способа сделать это на Java.

2

Лучшим и правильным юридическим способом тестирования частного Java-метода из тестовой среды является аннотация @VisibleForTesting по методу, поэтому тот же метод будет отображаться для тестовой среды как обычный публичный метод.

2

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

Я собираюсь сказать то, что нужно сказать, но многие могут не захотеть слышать. Те из вас, кто дает ответы, которые направляют автора вопроса к "обходному пути", оказывают серьезную медвежью услугу сообществу. Тестирование является основной частью всех инженерных дисциплин, вы не хотели бы покупать автомобиль, который не был должным образом протестирован и проверен осмысленно, так почему же кто-то захочет купить или использовать программное обеспечение, которое тестируется плохо? В любом случае, причина, по которой люди делают это, заключается в том, что последствия плохо протестированного программного обеспечения ощущаются не сразу, и мы обычно не связываем их с телесными повреждениями. Это очень опасное восприятие, которое будет трудно изменить, но мы несем ответственность за поставку безопасных продуктов независимо от того, что даже руководство заставляет нас делать это. Думаю, что Equifax взломать...

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

  • 2
    Вы делаете хорошие замечания (некоторые / большинство из которых уже были сделаны другими), но, пожалуйста, постарайтесь избежать антагонизма, используя такие слова, как «псевдо-разработчики / инженеры». Как вы сказали (и я согласен), мы не должны подвергать остракизму, а вместо этого создавать лучшее статус-кво и помогать всем учиться и совершенствоваться.
  • 1
    @Constantinos Спасибо за указание на это. Когда я писал этот ответ, я вспоминаю, что был разочарован отсутствием ресурсов (или их восприятием) для изучения передового опыта. Я едва закончил колледж в то время и искал критерий, который помог бы мне написать хороший код. Я согласен, что язык, который я использовал, был не в порядке, и я отредактировал это. Спасибо.
2

Что делать, если ваши тестовые классы находятся в том же пакете, что и класс, который должен быть протестирован?

Но в другом каталоге, конечно, src и classes для вашего исходного кода, test/src и test/classes для ваших тестовых классов. И пусть classes и test/classes находятся в вашем пути к классам.

  • 0
    тесты могут находиться в разных папках (например, src / test / java и код приложения в src / main / java), но пакет должен быть одинаковым.
2

В С# вы могли бы использовать System.Reflection, хотя в Java я не знаю. Хотя я чувствую желание ответить на это так или иначе, так как если вы "почувствуете, что вам нужно unit test частные методы", то я предполагаю, что есть что-то еще, что неправильно...

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

1

Моя команда и я используем Typemock, у которого есть API, который позволяет вам подделывать непубличные методы. Недавно они добавили возможность подделывать невидимые типы и использовать XUnit.

1

Вы можете использовать PowerMockito для установки значений возврата для частных полей и частных методов, которые вызываются/используются в приватном методе, который вы хотите проверить:

Eg. Установка возвращаемого значения для частного метода:

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
1

JML имеет синтаксис аннотации комментариев spec_public, который позволяет указать метод как открытый во время тестов:

private /*@ spec_public @*/ int methodName(){
...
}

Этот синтаксис обсуждается в http://www.eecs.ucf.edu/~leavens/JML/jmlrefman/jmlrefman_2.html#SEC12. Существует также программа, которая переводит спецификации JML в тесты JUnit. Я не уверен, насколько хорошо это работает или каковы его возможности, но он не кажется необходимым, поскольку JML является самодостаточной тестовой платформой.

0

Вы можете создать специальный публичный метод для прокси приватного метода для тестирования. Аннотация @TestOnly доступна из коробки при использовании IntelliJ. Недостатком является то, что если кто-то хочет использовать приватный метод в публичном контексте, он может это сделать. Но он будет предупрежден аннотацией и названием метода. На IntelliJ при этом появится предупреждение.

import org.jetbrains.annotations.TestOnly

class MyClass {

    private void aPrivateMethod() {}

    @TestOnly
    public void aPrivateMethodForTest() {
        aPrivateMethod()
    }
}
0

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

Что касается их использования, вы можете найти множество статей в Интернете. Вот тот, который мне особенно понравился:

0

Groovy имеет ошибка/функция, с помощью которой вы можете ссылаться на частные методы, как если бы они были общедоступными. Поэтому, если вы можете использовать Groovy в своем проекте, это вариант, который вы можете использовать вместо отражения. Посмотрите эту страницу для примера.

  • 2
    Это взлом не реальное решение.
  • 3
    @givanse Я не согласен ... это одна из причин, по которой тестирование Java-кода с использованием Groovy является настолько мощным.
0

Я тестирую только открытый интерфейс, но, как мне известно, я защищаю определенные частные методы, поэтому я могу полностью издеваться над ними или добавлять дополнительные шаги, специфичные для тестирования модулей. Общий случай заключается в подключении флагов, которые я могу установить из unit test, чтобы сделать определенные методы преднамеренно, чтобы исключение могло проверять пути ошибок; код запуска исключения только в тестовом пути в переопределенной реализации защищенного метода.

Я минимизирую необходимость в этом, и я всегда документирую точные причины, чтобы избежать путаницы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню