Я хочу проверить частный метод - что-то не так с моим дизайном?

Поэтому я чрезвычайно новичок в тестировании программного обеспечения, и я рассматриваю добавление нескольких тестов в одно из моих приложений. У меня есть открытый метод addKeywords(), который по пути вызывает частный метод removeInvalidOperations(). Этот частный метод вызывает вызовы внешнего API и составляет ~ 50 строк кода. Поскольку я считаю это несколько сложным методом, я бы хотел проверить это, не делая этого, вызвав метод addKeyword(). Однако это не представляется возможным (по крайней мере, хотя JUnit).

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

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

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

Ответ 1

Я предлагаю рефакторинг.

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

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

class YourClass
{
    private OperationRemover remover;

    public void addKeywords() {
        // whatever
        removeInvalidOperations();
    }

    private void removeInvalidOperations() {
         remover.remove();
    }
}

Это дает вам дополнительное преимущество в том, что вы можете в какой-то момент заменить эту зависимость, в том числе возможность протестировать ваш метод addKeywords() без фактического размещения внешнего вызова API, что облегчит тестирование этого метода. OperationRemover может быть интерфейсом, например, и для целей тестирования, вы просто передаете заглушку вместо нее, а не конкретную версию, используемую в процессе производства. Что касается вашей конкретной версии, вы можете писать тесты для нее независимо от того, что происходит с вашим существующим классом.

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

Более легкая тестируемость является побочным эффектом. Посмотрите на это по-другому: то, что вы на самом деле делаете, делает код свободно связанным и расширяемым. Выше мы отделили вызов от внешнего API от кода, который может потребовать использования результата. Внешний API может измениться. Вы можете перейти от одной службы к другой, но код, который использует результат, не должен заботиться. Этот класс может оставаться неизменным, только класс, который фактически размещает вызовы, должен быть модифицирован (или заменен).

Пример реального мира: год 2007 года, и вы работаете в банке в большом финансовом центре в Соединенных Штатах. Приложение должно использовать информацию об учетной записи. Ваш код добирается до какой-либо веб-службы внутри банка и получает необходимую информацию в той форме, в которой он нуждается, а затем продолжает свою обработку. В 2008 году финансовый сектор США развязывается, и ваш банк (который находится на грани краха) поглощается другим банком. Ваше приложение пощадилось, но теперь вам нужно обратиться к другому API, который уже существует в сохранившемся банке, чтобы получить информацию об учетной записи. Должен ли код, который потребляет эту информацию учетной записи, изменить? Не обязательно. Это та же самая информация учетной записи, что и раньше, только из другого источника. Нет, все, что нужно изменить, - это реализация, вызывающая API. Код потребления никогда не должен знать.

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

Ответ 2

Если он private, он не может считаться частью вашего API приложения, поэтому тестирование это действительно запах кода - когда тест прерывается, это нормально или нет?

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

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

Ответ 3

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

У вас может возникнуть соблазн проверить частный метод, если:

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

  • API вашего класса слишком негибкий. Общественным методам необходимо больше параметры или некоторые частные методы должны быть преданы гласности.

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

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

Для (1), очевидно, вы должны сначала попытаться найти способ проверить свой частный метод с существующими общедоступными методами.

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

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

Вместо того, чтобы думать в терминах "запахов кода", который является неточным и субъективным, вы можете думать в терминах скрытие информации. Решения, которые могут измениться, не должны раскрываться в вашем публичном API. Предпочтительно, проектные решения, которые могут измениться, также не должны подвергаться вашим блочным испытаниям - почему люди рекомендуют не тестировать частные методы.

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

Ответ 4

Если вы не хотите вызывать addKeywords(), возможно, вам следует просто добавить еще один общедоступный метод testRemoveInvalidOperations(), который просто вызывает private removeInvalidOperations(). Вы можете удалить тест позже.