Должен ли я тестировать частные методы или только публичные?

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

26 ответов


Я не тестирую частные методы. Частный метод-это деталь реализации, которая должна быть скрыта от пользователей класса. Тестирование частных методов нарушает инкапсуляцию.

Если я нахожу, что частный метод огромен или сложен или достаточно важен, чтобы требовать собственных тестов, я просто помещаю его в другой класс и делаю его общедоступным (Метод Объекта). Затем я могу легко проверить ранее-частный-но-теперь-публичный метод, который теперь живет сам по себе класс.


какова цель испытания?

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

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

рассмотрим: у вас есть открытый метод A, который вызывает частный метод B. A и B оба используют метод C. C изменяется (возможно, вами, возможно, поставщиком), в результате чего A начинает отказывать в своих тестах. Не было бы полезно иметь тесты для B также, хотя это личное, так что вы знаете, есть ли проблема в Использовать с, использовать B в C, или как?

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


Я склонен следовать совету Дэйва Томаса и Энди Ханта в их книге Прагматическое Модульное Тестирование:

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

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


Я чувствую себя вынужденным тестировать частные функции, поскольку я все больше и больше следую одной из наших последних рекомендаций QA в нашем проекте:

не более 10 в цикломатическая сложность на функции.

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


Да, я тестирую частные функции, потому что, хотя они тестируются вашими общедоступными методами, в TDD (Test Driven Design) приятно протестировать наименьшую часть приложения. Но частные функции недоступны, когда вы находитесь в своем классе тестового блока. Вот что мы делаем, чтобы проверить наши частные методы.

Почему у нас есть частные методы?

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

когда мы кодируем, мы используем test-driven-design (TDD). Это означает, что иногда мы натыкаемся на часть функциональности, которая является частной и хочет проверить. Частные функции не тестируются в phpUnit, потому что мы не можем получить к ним доступ в тестовом классе (они частный.)

мы думаем, что вот 3 решения:

1. Вы можете проверить свои интимные с помощью ваших публичных методов

преимущества

  • простое модульное тестирование (никаких "хаков" не требуется)

недостатки

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

2. Если private настолько важен, то, возможно, это codesmell для создания нового отдельного класса для него

преимущества

  • вы можете выполнить рефакторинг это новый класс, потому что если это важно, другие классы могут нуждаться в этом тоже
  • тестируемая группа открытого метода, так тестируемый

недостатки

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

3. Измените модификатор доступа на (final) protected

преимущества

  • вы испытываете самую маленькую тестируемую часть приложение. Когда используя final protected, функция не будет переопределяемой (просто как рядовой)
  • нет потерь
  • никаких дополнительных накладных расходов

недостатки

  • вы меняете Частный доступ к защищенному, что означает, что это доступный его детей
  • вам все еще нужен макет класса в вашем тестовом классе для использования это

пример

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

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


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

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


Если частный метод хорошо определен (т. е. он имеет функцию, которая проверяема и не предназначена для изменения с течением времени), то да. Я проверяю все, что поддается проверке, там, где это имеет смысл.

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

Это ускоряет отладку позже.

Адам


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


Если вы разрабатываете test driven (TDD), вы будете тестировать свои частные методы.


мне не нравится тестировать частную функциональность по нескольким причинам. Они заключаются в следующем (это основные моменты для людей TLDR):

  1. обычно, когда вы испытываете соблазн проверить частный метод класса, это дизайнерский запах.
  2. вы можете проверить их через общественность интерфейс (именно так вы хотите их протестировать, потому что именно так клиент будет вызывать / использовать их). Вы можете получить ложное чувство безопасности видя зеленый свет на всех проходящих тесты для частного методы. Гораздо лучше / безопаснее тестировать крайние случаи на ваших частных функциях через ваш открытый интерфейс.
  3. вы рискуете серьезным дублированием теста (тесты, которые выглядят / чувствуют очень похоже) путем тестирования частных методов. Это имеет большое последствия при изменении требований, как много больше тестов, чем необходимо будет разбить. Она также может поставить вас в положение, где это трудно рефакторинг из-за ваших тестов...что является предельным ирония, потому что набор тестов там, чтобы помочь вам безопасно перепроектировать и рефакторинг!

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

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

я также перехожу к тому, почему TDD не является допустимое оправдание для тестирования частных методов в самом конце.

рефакторинг вашего выхода из плохого дизайна

один из самых распространенных (анти)отцов, которые я вижу, что Майкл Перья звонки класс "Айсберг" (если вы не знаете, кто такой Майкл перья, купите / прочитайте его книгу "эффективная работа с устаревшим кодом". Он человек, о котором стоит знать, если вы профессиональный инженер-программист/разработчик). Есть другие (анти)шаблоны, которые вызывают эту проблему, но это, безусловно, самый распространенный, на который я наткнулся. Классы" айсберга " имеют один публичный метод, а остальные являются частными (поэтому возникает соблазн проверить частные методы). Он называется классом "Айсберг", потому что обычно появляется одинокий публичный метод, но остальная функциональность скрыта под водой в виде частных методов. Это может выглядеть примерно так это:

Rule Evaluator

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

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

Ну, это на самом деле выглядит довольно хорошо. Мы хотели бы убедиться, что мы сохраняем это поведение при внесении изменений. Но!--4--> это частная


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


мы тестируем частные методы путем вывода, под которым я подразумеваю, что мы ищем общий охват тестирования класса не менее 95%, но только наши тесты вызывают публичные или внутренние методы. Чтобы получить покрытие, нам нужно сделать несколько звонков в public / internals на основе различных сценариев, которые могут произойти. Это делает наши тесты более intentful вокруг цель кода, который они тестируют.

Trumpi на пост вы связаны-это лучший.

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


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

я наткнулся на два сообщения, которые, я думаю, решают эту проблему достаточно тщательно в случае TDD.

  1. тестирование частных методов, TDD и тестового рефакторинга
  2. разработка с тестовым приводом не тестируется

В Итоге:

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

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

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

таким образом, эти методы будут общедоступными, и их тестирование будет достаточно простым.

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


Как указано выше, " если вы не проверяете свои частные методы, как вы знаете, что они не сломаются?"

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

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

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

Итак, в общем, да, модульный тест ваших частных методов.


Если вы не проверяете свои частные методы, откуда вы знаете, что они не сломаются?


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


Это, очевидно, зависит от языка. В прошлом с C++ я объявил класс тестирования классом друга. К сожалению, это требует, чтобы ваш производственный код знал о классе тестирования.


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

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

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

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

  • вызывается ли метод более одного раза из разных мест?
  • является ли метод достаточно сложным, чтобы требовать тестов?

Если мы проверяем правильность логики, а частный метод несет логику, мы должны проверить ее. Не так ли? Так почему мы это пропустим?

написание тестов, основанных на видимости методов, совершенно неуместно.


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


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


Абсолютно да. В этом смысл модульного тестирования, вы тестируете единицы. Частный метод-это единица. Без тестирования частных методов TDD (Test Driven Development) было бы невозможно,


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


ответ на вопрос " Должен ли я тестировать частные методы?" есть." ......иногда." Как правило, вы должны тестировать интерфейс своих классов.

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

здесь пример:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

на RefactoredThing теперь у вас есть 5 тестов, 2 из которых вам пришлось обновить для рефакторинга, но функциональность вашего объекта действительно не изменилась. Итак, предположим, что вещи сложнее, чем это, и у вас есть какой-то метод, который определяет порядок вывода, такой как:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

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

и, наконец, предположим, что ваш основной объект очень тяжелый, а метод довольно маленький, и вам действительно нужно убедиться, что вывод правильный. Вы думаете: "я должен проверить этот частный метод!". Может быть, вы можете сделать свой объект легче, передав часть тяжелой работы в качестве параметра инициализации? Затем вы можете пройти что-то более легкое и протестировать против что.


нет, вы не должны тестировать частные методы почему? и, кроме того, популярный издевательский фреймворк, такой как Mockito, не поддерживает тестирование частных методов.