@KaminskyIlya

Как тестировать закрытые методы класса?

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

public class PersonDate
{
public PersonDate(Date birthday) { ... }

public int getYearsOld() 
{
  return isValid() ? getYear( getNextBirthdayDate() ) - getYear( getBornDate() ) + 1 : 0; 
}
// (TODO: нужен тест) возвращает год для даты
private static int getYear( Date date ) { ... }
// (TODO: нужен тест) возвращает дату следующего дня рождения
private Date getNextBirthdayDate() { ... }


Нужно убедиться, что закрытые вспомогательные функции getYear() и getNextBirthdayDate() правильно отрабатывают. Собственно, как протестировать их работу в JUnit или любом другом подобном фреймворке ?

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

1. Способ первый.
Метод getYear() объявлен статическим. Его можно вынести в публичный вспомогательный класс, либо внутренний, либо внешний:
static final class DateUtils
{
public static int getYear( Date date ) { ... }
}

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

2. Способ второй.
Объявить тестируемый метод с уровнем доступа package. Это сработает, т.к. тестирующий класс компилируется с тестируемым в одном и том же пакете. Но изменять область видимости только ради написания теста... хм...

3. Способ третий. Давно забытое старое.
Написать тестирующий класс внутри тестируемого:
public class PersonDate
{
...
public static class PersonDateTest
{
    @Test
    public testGetNextBirthDate()
    {
    }
}

В этом случае тестирующий класс получает полный доступ ко всем закрытым полям объекта-владельца.
Но вроде как, подобный подход - уже не по мейнстриму? Кажется ранее (до JUnit и подобных фреймворков), именно такая методика написания тестов и была основной. Внутренние тестовые классы при компиляции превращаются в отдельные файлы вида PersonDate$PersonDateTest.class и затем легко могут быть удалены из продакшн средствами сборки.

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

Буду благодарен за все ваши ответы.
  • Вопрос задан
  • 4168 просмотров
Решения вопроса 1
mrRontgen
@mrRontgen
Scala lover.
Коллеги, может быть кто знает другие более красивые способы?
- через рефлекшен! Если же у Вас есть возможность изменить область видимости, то тогда не стоит его использовать. Если такой возможности нет, то вот Вам паттерн:
Method method = PersonDate.class.getDeclaredMethod("getYear", Int.class);
        method.setAccessible(true);
        method.invoke(targetObject, argObjects);
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы