Junit & java: тестирование непубличных методов

JUnit будет проверять только те методы в моем классе, которые являются общедоступными. Как выполнить тестирование юнита на тех, которые не являются (например, частными, защищенными)?

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

Ответ 1

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

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

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

Надеюсь, что это поможет.

Ответ 2

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

Если это не работает, JUnitX позволяет тестировать частные методы, хотя я считаю, что он доступен только для JUnit 3.8.

Ответ 3

Когда вы пишете тест JUnit, вам нужно сделать тонкий сдвиг ума: "Я теперь клиент своего класса". Это означает, что private является приватным, и вы проверяете только поведение, которое видит клиент.

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

За три года, прошедшие с тех пор, как я изначально написал это, я немного по-разному подошел к проблеме, используя отражение Java.

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

public void testPrivateField() throws InterruptedException {
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class;
    try {
        Field privateField = clazz.getDeclaredField("nameOfPrivateField");
        privateField.setAccessible(true); // This is the line
        // do stuff
    } catch(NoSuchFieldException nsfe) {
        nsfe.printStackTrace();
        fail();
    } catch(IllegalAccessException iae) {
        iae.printStackTrace();
        fail();
    }

}

Ответ 7

Я почти всегда использую Spring в моих проектах Java и, как таковые, мои объекты создаются для инъекции зависимостей. Они, как правило, представляют собой довольно гранулированные реализации публичных интерфейсов, которые собраны в контексте приложения. Таким образом, я редко (если когда-либо) испытываю необходимость проверять частные методы, потому что сам класс достаточно мал, что это просто не проблема.

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

По моему опыту, необходимость использования unit test частных методов является индикатором того, что то, что вы просматриваете, может (и должно) быть упрощено.

Это, если вы все еще чувствуете необходимость:

  • Защищенные методы могут быть проверены подклассами;
  • Пакет частных методов можно протестировать, поставив модульные тесты в одном пакете; и
  • Частные методы могут быть проверены модулем, предоставляя, например, частный factory метод прокси-сервера. Не идеальный, но частный личный характер.

Ответ 8

Обычно вы не тестируете частные методы, потому что они могут (как правило) тестироваться косвенно через другой общедоступный метод. Когда вы тестируете вождение и делаете частные методы, они обычно являются результатом рефакторинга метода "extract method" и уже тестируются косвенно.

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

Ответ 9

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

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

public class HolderOfSomeStrings{

    private List<String> internal_values;

    public List<String> get()
    {
       List<String> out = new ArrayList<String>();
       for (String s:internal_values)
       { 
           out.add(process(s));
       }
      return get;
    }

   private static String process(String input)
   {
     //do something complicated here that other classes shouldn't be interested in
    }

}

Дело здесь в том, что junit заставляет меня сделать процесс общедоступным или, по крайней мере, защищенным, или поместить его в свой собственный класс утилиты. Но если это какая-то внутренняя логика HolderOfSomeStrings, мне совершенно не ясно, что это правильно - мне кажется, что это должно быть частным, и в какой-то мере сделать его более заметным.

Ответ 10

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

Предположим, что у вас есть общедоступный метод pubMethod и частный метод privMethod. Когда вы вызываете pubMethod, он, в свою очередь, вызывает privMethod для выполнения задачи (возможно, синтаксический анализ строки). Затем pubMethod использует эту анализируемую строку для определения значений переменных-членов каким-либо образом или для влияния на свое собственное возвращаемое значение. Протестируйте, наблюдая за желаемым эффектом на возвращаемое значение pubMethod или на переменные-члены (возможно, используя accessors, чтобы добраться до них).

Ответ 11

DP4j Jar

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

Теперь эта задача упрощается с помощью Dp4j jar.

  • Dp4j анализирует ваш код и автоматически генерирует для вас код API Reflection.

  • Просто добавьте dp4j.jar в свой CLASSPATH.

  • Dp4j.jar содержит Обработчики аннотаций, они будут искать методы в вашем коде, которые аннотируются аннотацией @Test JUnit.

  • Dp4j проанализирует код этих методов, и если он обнаружит, что вы незаконно обращаетесь к закрытым методам, он заменит вашу неверную личную ссылку на метод эквивалентным кодом, использующим API Java Reflection.

Получить подробнее здесь

Ответ 12

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

Ответ 13

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

Ответ 14

Найдите "PrivateAccessor.invoke". Мой код импортирует его из "junitx.util", но я не знаю, откуда он.

Ответ 15

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

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

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

Ответ 16

В качестве своего рода ответвление, и я не уверен, где все сводятся к проблеме "программирование полиглота", но тесты Groovy выполняются в Junit и игнорируют весь публичный/непубличный вопрос, Сторона примечания, она была официально классифицирована как "ошибка", но когда они пытались ее исправить, там была такая буря, что она была отброшена, как это было первоначально.

Ответ 17

Я абсолютно согласен с @duffymo, что код должен быть проверен с точки зрения клиента (хотя он говорит, что он так не думал об этом). Однако, с этой точки зрения, частные и другие имеют разные значения. Клиент частного метода - это сам класс, поэтому я предпочитаю тестировать их через внешний (общедоступный/защищенный пакетом) API. Тем не менее, защищенные и защищенные пакетом члены существуют для внешних клиентов, поэтому я проверяю их на подделки, которые наследуют класс-владелец или находятся в одном пакете.