it-swarm.com.ru

junit & Java: тестирование закрытых методов

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

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

91
jbu

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

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

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

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

86
MattK

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

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

36
Kent Beck

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

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

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

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

25
duffymo

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

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

9
starblue

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

9
marcumka

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

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

}
8
Cheese Daneish

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

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

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

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

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

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

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

3
Spoike

DP4j Jar

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

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

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

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

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

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

Получить более подробную информацию здесь

2
VdeX

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

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

2
Wil Doane

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

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

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, мне совсем не ясно, что это правильно - мне кажется, что это должно быть приватно, и что делает его более заметным, код каким-то образом смешивается. 

2
Steve B.

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

1
gyanendra kushwaha

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

1
Pierre Gardin

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

0
Cagatay Kalan

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

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

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

0
Bill K

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

0
Paul Tomblin

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

0
mezmo