Как проверить метод void, который изменяет только переменные частного класса?

Я пытаюсь использовать unit test метод в классе, который инициализирует некоторые частные поля:

public void init(Properties props) throws Exception {

    this.language = props.getProperty(Constants.LANGUAGE,Constants.LANGUAGE_DEFAULT);   
    this.country = props.getProperty(Constants.COUNTRY,Constants.COUNTRY_DEFAULT);

        try {
            this.credits = Integer.valueOf(props.getProperty(Constants.CREDITS_OPTION_NAME, Constants.CREDITS_DEFAULT_VALUE));
        } catch (NumberFormatException e) {
            throw new Exception("Invalid configuration: 'credits' does not contain a valid integer value.", e);
        }
        //rest of method removed for sake of simplicity
    }

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

  • Сделать общедоступными методы для доступа к закрытым полям, затем вызвать эти методы после тестирования метода init.
  • Сделайте метод unit test только для вызова вызова, и предположим, что все работало правильно, не было исключено исключение.

Как вы думаете, это идеальный способ протестировать этот метод?

Ответ 1

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

Field field = YourClass.class.getDeclaredField("language");
field.setAccessible(true); //override the access restriction of it being private
field.get(yourObject);

Если там есть spring на пути к классу, вы можете использовать его ReflectionUtils/ReflectionTestUtils.

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

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

Ответ 2

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

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

Другой плакат предложил использовать рефлексию для доступа к закрытым полям. Я советую против этого; все, что помечено как частное, должно быть детальностью реализации класса и поэтому не должно быть непосредственно протестировано. Вместо этого вы должны проверить опубликованный API своего класса. Тогда у вас есть свобода реорганизовать его легко, изменив детали (частные).

Ответ 3

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

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

Ответ 4

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

Открытый аксессор для частных методов также является хорошим выбором.

Ответ 5

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

BTW: Альтернативой публичным getters является создание unit test в одном пакете и предоставление локальных getters пакетов.