Сделать приватный метод общедоступным для модульного тестирования ... хорошая идея? - PullRequest
279 голосов
/ 16 августа 2011

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


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

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

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

ОБНОВЛЕНИЕ

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

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

Ответы [ 33 ]

3 голосов
/ 28 июля 2015

Вы никогда не должны позволять своим тестам диктовать ваш код. Я не говорю о TDD или других DD, я имею в виду именно то, что вы просите. Нужны ли вашему приложению эти методы, чтобы быть публичными? Если это так, то проверьте их. Если этого не произойдет, не публикуйте их только для тестирования. То же самое с переменными и другими. Пусть потребности вашего приложения диктуют код, и пусть ваши тесты проверяют, что потребность удовлетворена. (Опять же, я не имею в виду сначала тестирование или нет, я имею в виду изменение структуры классов для достижения цели тестирования).

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

Например (псевдокод здесь);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

Нет причин проверять «добавить», вместо этого вы можете проверить «книги».

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

2 голосов
/ 17 августа 2011

Guava имеет аннотацию @VisibleForTesting для маркировки методов, которые увеличили область действия (пакет или общедоступный), что они иначе.Я использую аннотацию @Private для той же вещи.

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

Когда:

  • класс становится значительно менее читаемым, в целом, разбивая его на несколько классов,
  • просто для того, чтобы сделать его более тестируемым,
  • и предоставление некоторого тестового доступа к внутренностям сделало бы уловку

кажется, что религия превосходит инженерию.

2 голосов
/ 16 августа 2011

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

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

2 голосов
/ 28 июля 2015

Нет, потому что есть лучшие способы снятия шкур с этого кота.

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

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

Или вы выбираете вариант «друг».Лично это особенность C ++, которая мне нравится меньше всего, потому что она нарушает правила инкапсуляции, но это необходимо для того, как C ++ реализует некоторые функции, так что, эй, хо.

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

2 голосов
/ 16 августа 2011

Я часто добавляю метод, называемый чем-то вроде validate, verify, check и т. Д., В класс, чтобы его можно было вызывать для проверки внутреннего состояния объекта.

Иногда этот метод помещается в блок ifdef (я пишу в основном на C ++), чтобы он не компилировался для выпуска.Но в выпуске часто полезно предоставлять методы проверки, которые обходят деревья объектов программы, проверяя вещи.

2 голосов
/ 16 августа 2011

Я обычно держу тестовые классы в том же проекте / сборке, что и тестируемые классы.
Таким образом, мне нужно только internal видимость, чтобы сделать функции / классы тестируемыми.

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

Это, конечно, относится только к C # / .NET-части вашего вопроса

2 голосов
/ 02 мая 2014

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

1 голос
/ 16 августа 2011

Как широко отмечается в комментариях других, модульные тесты должны быть ориентированы на общедоступный API.Тем не менее, за и против и в сторону оправдания, вы можете вызывать закрытые методы в модульном тесте, используя отражение.Вы, конечно, должны убедиться, что ваша безопасность JRE позволяет это.Вызов частных методов - это то, что Spring Framework использует с ReflectionUtils (см. Метод makeAccessible(Method)).

Вот небольшой пример класса с методом закрытого экземпляра.

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

И пример класса, который выполняет метод частного экземпляра.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

При выполнении B будет напечатано Doing something private. Если вам это действительно нужно, отражение может использоваться в модульных тестах для доступа к методам частного экземпляра..

1 голос
/ 24 июня 2016

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

Подробнее об этом можно узнать на MSDN или здесь на Переполнение стека

(Мне интересно, что никто до сих пор не упомянул об этом.)

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

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

0 голосов
/ 16 августа 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...