Я прочитал этот пост о том, как тестировать частные методы. Я обычно не проверяю их, потому что всегда думал, что быстрее проверять только публичные методы, которые будут вызываться извне объекта. Вы тестируете частные методы? Я должен всегда проверять их?
unit-testing
testing
language-agnostic
Патрик Дежарден
источник
источник
Ответы:
Я не тестирую приватные методы. Закрытый метод - это деталь реализации, которая должна быть скрыта для пользователей класса. Тестирование частных методов нарушает инкапсуляцию.
Если я нахожу, что закрытый метод является огромным, сложным или достаточно важным, чтобы требовать собственных тестов, я просто помещаю его в другой класс и делаю его общедоступным ( метод Object ). Затем я могу легко протестировать ранее приватный, но теперь публичный метод, который теперь живет в своем собственном классе.
источник
Какова цель тестирования?
Большинство ответов пока говорят, что частные методы - это детали реализации, которые не имеют (или, по крайней мере, не должны) иметь значение, если публичный интерфейс хорошо протестирован и работает. Это абсолютно правильно, если ваша единственная цель для тестирования - гарантировать, что публичный интерфейс работает .
Лично я в основном использую тесты кода, чтобы гарантировать, что будущие изменения кода не вызовут проблем, и помочь моим усилиям по отладке, если они это сделают. Я считаю, что тестирование приватных методов так же тщательно, как и публичный интерфейс (если не больше!) Способствует этой цели.
Обратите внимание: у вас есть открытый метод A, который вызывает закрытый метод B. A и B оба используют метод C. C изменяется (возможно, вами, возможно, поставщиком), в результате чего A начинает проваливать свои тесты. Разве не было бы полезно иметь тесты на B также, хотя он и частный, чтобы вы знали, заключается ли проблема в том, что A использует C, B использует C или оба?
Тестирование частных методов также повышает ценность в тех случаях, когда тестовое покрытие открытого интерфейса является неполным. Несмотря на то, что мы обычно хотим избежать этой ситуации, единичное тестирование эффективности зависит как от тестов, обнаруживающих ошибки, так и от связанных с этим затрат на разработку и обслуживание этих тестов. В некоторых случаях преимущества 100% покрытия тестами могут быть сочтены недостаточными, чтобы оправдать затраты на эти тесты, что приводит к пробелам в покрытии тестов общедоступного интерфейса. В таких случаях хорошо нацеленный тест частного метода может быть очень эффективным дополнением к базе кода.
источник
testDoSomething()
testDoSomethingPrivate()
пройден, мы не знаем напрямую, где находится основная причина сбоя. Это может быть либо либо, либо . Это делает тест менее ценным. , Вот еще несколько причин для тестирования частного stackoverflow.com/questions/34571 /Я склонен следовать советам Дейва Томаса и Энди Ханта в их книге « Прагматическое модульное тестирование» :
Но иногда я не могу удержаться от тестирования частных методов, потому что это дает мне чувство уверенности в том, что я создаю полностью надежную программу.
источник
Я чувствую себя обязанным тестировать частные функции, поскольку я все больше и больше следую одной из наших последних рекомендаций по обеспечению качества в нашем проекте:
Теперь побочный эффект от применения этой политики состоит в том, что многие из моих очень больших публичных функций разделены на многие более сфокусированные, лучше названные частные функции.
Публичная функция все еще существует (конечно), но по существу сводится к тому, чтобы называть все эти частные «подфункциями»
Это на самом деле круто, потому что callstack теперь намного легче читать (вместо ошибки в большой функции, у меня есть ошибка в подподфункции с именем предыдущих функций в callstack, чтобы помочь мне понять «как я туда попал»)
Однако теперь кажется, что проще выполнить модульное тестирование непосредственно этих закрытых функций и оставить тестирование большой общедоступной функции в каком-то «интеграционном» тесте, где необходимо рассмотреть сценарий.
Просто мои 2 цента.
источник
Да, я тестирую частные функции, потому что, хотя они тестируются вашими общедоступными методами, в TDD (Test Driven Design) было бы неплохо протестировать наименьшую часть приложения. Но частные функции недоступны, когда вы находитесь в классе тестового модуля. Вот что мы делаем, чтобы проверить наши частные методы.
Почему у нас есть частные методы?
Закрытые функции в основном существуют в нашем классе, потому что мы хотим создать читаемый код в наших открытых методах. Мы не хотим, чтобы пользователь этого класса вызывал эти методы напрямую, а через наши открытые методы. Кроме того, мы не хотим изменять их поведение при расширении класса (в случае защищенного), поэтому он является приватным.
Когда мы кодируем, мы используем тестируемый дизайн (TDD). Это означает, что иногда мы сталкиваемся с частной функциональностью и хотим протестировать ее. Закрытые функции не тестируются в phpUnit, потому что мы не можем получить к ним доступ в классе Test (они являются закрытыми).
Мы думаем, что есть 3 решения:
1. Вы можете проверить свои ряды с помощью публичных методов
преимущества
Недостатки
2. Если приватность так важна, то, возможно, это кодовая ячейка, чтобы создать для нее новый отдельный класс
преимущества
Недостатки
3. Измените модификатор доступа на (окончательный) защищенный
преимущества
Недостатки
пример
Таким образом, наш тестовый модуль теперь может вызывать test_sleepWithSuspect для проверки нашей прежней частной функции.
источник
Я не люблю тестировать закрытые функции по нескольким причинам. Они заключаются в следующем (это основные моменты для людей TLDR):
Я объясню каждый из них на конкретном примере. Оказывается, что 2) и 3) несколько запутанно связаны, поэтому их пример похож, хотя я рассматриваю их как отдельные причины, по которым вам не следует тестировать частные методы.
Бывают случаи, когда уместно тестировать приватные методы, просто важно знать о недостатках, перечисленных выше. Я собираюсь обсудить это более подробно позже.
Я также расскажу, почему TDD не является оправданием для тестирования частных методов в самом конце.
Рефакторинг вашего выхода из плохого дизайна
Один из самых распространенных (анти) паттернов, который я вижу, - это то, что Майкл Фезерс называет классом «Айсберг» (если вы не знаете, кто такой Майкл Фезерс, иди купите / прочитайте его книгу «Эффективная работа с устаревшим кодом»). человек, которого стоит знать, если вы профессиональный инженер / разработчик программного обеспечения). Существуют и другие (анти) паттерны, которые приводят к возникновению этой проблемы, но на данный момент это самая распространенная проблема, с которой я столкнулся. У классов "Айсберг" есть один публичный метод, а остальные - частные (вот почему заманчиво тестировать приватные методы). Он называется классом «Айсберг», потому что обычно выявляется одинокий публичный метод, но остальная часть функций скрыта под водой в виде частных методов.
Например, вы можете захотеть проверить,
GetNextToken()
последовательно вызвав его в строку и убедившись, что он возвращает ожидаемый результат. Такая функция заслуживает проверки: это поведение не тривиально, особенно если ваши правила токенизации сложны. Давайте представим, что это не так уж сложно, и мы просто хотим связать токены, разделенные пробелом. Итак, вы пишете тест, возможно, он выглядит примерно так (некоторый не зависящий от языка псевдо-код, надеюсь, идея ясна):Ну, это на самом деле выглядит довольно мило. Мы хотели бы убедиться, что мы поддерживаем это поведение при внесении изменений. Но
GetNextToken()
это частная функция! Поэтому мы не можем протестировать его таким образом, потому что он даже не будет компилироваться (при условии, что мы используем какой-то язык, который на самом деле реализует public / private, в отличие от некоторых языков сценариев, таких как Python). Но как насчет измененияRuleEvaluator
класса в соответствии с принципом единой ответственности (принцип единой ответственности)? Например, у нас, похоже, есть парсер, токенизатор и оценщик, объединенные в один класс. Не лучше ли разделить эти обязанности? ВдобавокTokenizer
ко всему , если вы создадите класс, то это будут открытые методыHasMoreTokens()
иGetNextTokens()
.RuleEvaluator
Класс может иметьTokenizer
объект как член. Теперь мы можем сохранить тот же тест, что и выше, за исключением того, что мы тестируемTokenizer
класс вместоRuleEvaluator
класса.Вот как это может выглядеть в UML:
Обратите внимание, что этот новый дизайн увеличивает модульность, так что вы можете потенциально использовать эти классы в других частях вашей системы (прежде чем вы не смогли, частные методы не могут быть повторно использованы по определению). Это является основным преимуществом разрушения RuleEvaluator, наряду с повышенной понятностью / локальностью.
Тест выглядел бы чрезвычайно похожим, за исключением того, что он на самом деле компилируется на этот раз, так как
GetNextToken()
метод теперь открыт дляTokenizer
класса:Тестирование приватных компонентов через публичный интерфейс и предотвращение дублирования тестов
Даже если вы не думаете, что можете разбить свою проблему на меньшее количество модульных компонентов (что вы можете сделать в 95% случаев, если просто попытаетесь это сделать), вы можете просто протестировать частные функции через открытый интерфейс. Часто частные члены не стоит тестировать, потому что они будут протестированы через открытый интерфейс. Часто я вижу тесты, которые выглядят очень похоже, но тестируют две разные функции / методы. В конечном итоге происходит то, что когда требования меняются (а они всегда меняются), у вас теперь есть 2 неработающих теста вместо 1. И если вы действительно проверили все свои частные методы, у вас может быть больше как 10 неработающих тестов вместо 1. Короче говоря , тестирование частных функций (с помощью
FRIEND_TEST
или делая их публично или с использованием отражения) , которые в противном случае можно было бы проверить через публичный интерфейс может вызвать тест дублирования . Вы действительно не хотите этого, потому что ничто не повредит больше, чем ваш набор тестов, замедляющий вас. Это должно сократить время разработки и снизить затраты на обслуживание! Если вы тестируете частные методы, которые в противном случае тестируются через открытый интерфейс, набор тестов вполне может сделать обратное и активно увеличить затраты на обслуживание и увеличить время разработки. Когда вы делаете приватную функцию общедоступной, или если вы используете что-то вродеFRIEND_TEST
и / или отражения, вы, как правило, в конечном итоге пожалеете об этом в долгосрочной перспективе.Рассмотрим следующую возможную реализацию
Tokenizer
класса:Допустим,
SplitUpByDelimiter()
он отвечает за возврат массива, так что каждый элемент массива является токеном. Кроме того, давайте просто скажем, чтоGetNextToken()
это просто итератор для этого вектора. Итак, ваш публичный тест может выглядеть так:Давайте представим, что у нас есть то, что Майкл Фезер называет инструментом поиска . Это инструмент, который позволяет вам прикоснуться к частным частям других людей. Пример
FRIEND_TEST
из GoogleTest, или рефлексия, если язык поддерживает это.Хорошо, теперь давайте скажем, что требования меняются, и токенизация становится намного более сложной. Вы решаете, что простого разделителя строк не будет достаточно, и вам нужен
Delimiter
класс для обработки задания. Естественно, вы ожидаете, что один тест сломается, но эта боль усиливается, когда вы тестируете частные функции.Когда может быть уместным тестирование частных методов?
В программном обеспечении нет «одного размера для всех». Иногда нормально (и на самом деле идеально) «нарушать правила». Я настоятельно рекомендую не тестировать закрытые функции, когда это возможно. Есть две основные ситуации, когда я думаю, что все в порядке:
Я много работал с унаследованными системами (именно поэтому я такой большой поклонник Майкла Фезерса), и я могу с уверенностью сказать, что иногда просто безопаснее всего протестировать приватную функциональность. Это может быть особенно полезно для получения «тестов характеристик» в базовой линии.
Вы спешите, и вам нужно сделать как можно быстрее здесь и сейчас. В конце концов, вы не хотите тестировать частные методы. Но я скажу, что обычно требуется некоторое время на рефакторинг для решения проблем проектирования. И иногда вы должны отправить через неделю. Ничего страшного: делайте все быстро и грязно и тестируйте частные методы, используя инструмент поиска, если вы считаете, что это самый быстрый и надежный способ выполнить свою работу. Но поймите, что то, что вы сделали, было неоптимальным в долгосрочной перспективе, и, пожалуйста, рассмотрите возможность вернуться к нему (или, если об этом забыли, но вы увидите это позже, исправьте это).
Возможно, есть и другие ситуации, когда все в порядке. Если вы думаете, что все в порядке, и у вас есть хорошее оправдание, тогда сделайте это. Никто не останавливает вас. Просто будьте в курсе потенциальных затрат.
Оправдание TDD
Кроме того, я действительно не люблю людей, использующих TDD в качестве оправдания для тестирования частных методов. Я практикую TDD, и я не думаю, что TDD заставляет вас делать это. Вы можете сначала написать свой тест (для вашего открытого интерфейса), а затем написать код, удовлетворяющий этому интерфейсу. Иногда я пишу тест для общедоступного интерфейса, и я удовлетворяю его, написав также один или два небольших приватных метода (но я не тестирую приватные методы напрямую, но я знаю, что они работают, или мой публичный тест провалится ). Если мне нужно протестировать крайние случаи этого закрытого метода, я напишу целую кучу тестов, которые будут проходить через мой открытый интерфейс.Если вы не можете понять, как добиться успеха в крайних случаях, это сильный знак того, что вам нужно реорганизовать небольшие компоненты, каждый со своими открытыми методами. Это признак того, что частные функции делают слишком много и выходят за рамки класса .
Кроме того, иногда я нахожу, что пишу тест, который слишком укусит, чтобы жевать в данный момент, и поэтому я думаю: «э, я вернусь к этому тесту позже, когда у меня будет больше API для работы» (я закомментирую и буду держать это в голове). Именно здесь многие разработчики, которых я встречал, начнут писать тесты для своей частной функциональности, используя TDD в качестве козла отпущения. Они говорят: «О, ну, мне нужен какой-то другой тест, но для написания этого теста мне понадобятся эти частные методы. Поэтому, поскольку я не могу написать производственный код без написания теста, мне нужно написать тест». для частного метода. " Но то, что им действительно нужно сделать, - это рефакторинг на более мелкие и повторно используемые компоненты вместо того, чтобы добавлять / тестировать кучу частных методов в их текущий класс.
Примечание:
Я недавно ответил на аналогичный вопрос о тестировании частных методов с помощью GoogleTest . Я в основном изменил этот ответ, чтобы сделать его более независимым от языка.
PS Вот соответствующая лекция Майкла Фезерса об уроках айсберга и инструментах поиска: https://www.youtube.com/watch?v=4cVZvoFGJTU
источник
_
, это означает «эй, это« частный ». Вы можете использовать его, но полное раскрытие, он не предназначен для повторного использования, и вы должны использовать его, только если вы действительно знаю, что ты делаешь ". Вы можете использовать тот же подход на любом языке: сделать этих участников публичными, но пометить их ведущими_
. Или, может быть, эти функции действительно должны быть закрытыми и просто протестированы через общедоступный интерфейс (подробности см. В ответе). Это в каждом конкретном случае, нет общего правилаЯ думаю, что лучше всего просто проверить открытый интерфейс объекта. С точки зрения внешнего мира, имеет значение только поведение открытого интерфейса, и именно на это должны быть направлены ваши юнит-тесты.
Как только у вас есть твердые модульные тесты, написанные для объекта, вам не нужно возвращаться и изменять эти тесты только потому, что изменилась реализация интерфейса. В этой ситуации вы нарушили последовательность вашего модульного тестирования.
источник
Если ваш приватный метод не проверяется путем вызова ваших публичных методов, то что он делает? Я говорю приват, не защищенный или друг.
источник
Если закрытый метод хорошо определен (т. Е. У него есть функция, которая может быть проверена и не должна изменяться со временем), тогда да. Я проверяю все, что можно проверить, где это имеет смысл.
Например, библиотека шифрования может скрывать тот факт, что она выполняет блочное шифрование с помощью закрытого метода, который шифрует только 8 байтов за раз. Я бы написал для этого модульный тест - он не предназначен для изменения, даже если он скрыт, и если он ломается (например, из-за будущих улучшений производительности), то я хочу знать, что сломалась не только частная функция, но и не только что одна из общественных функций сломалась.
Это ускоряет отладку позже.
-Адам
источник
Если вы разрабатываете тест-драйв (TDD), вы будете тестировать свои частные методы.
источник
Я не эксперт в этой области, но модульное тестирование должно проверять поведение, а не реализацию. Частные методы являются строго частью реализации, поэтому ИМХО не следует тестировать.
источник
Мы тестируем частные методы с помощью логического вывода, и я имею в виду, что мы ищем общий охват тестов класса, по крайней мере, на 95%, но наши тесты только обращаются к открытым или внутренним методам. Чтобы получить покрытие, нам нужно сделать несколько звонков для общественности / внутренних органов в зависимости от возможных сценариев. Это делает наши тесты более внимательными к цели кода, который они тестируют.
Ответ Трампи на пост, на который вы ссылаетесь, является лучшим.
источник
Модульные тесты, на мой взгляд, предназначены для тестирования общедоступных методов. Ваши публичные методы используют ваши приватные методы, поэтому косвенно они также проходят тестирование.
источник
Я некоторое время размышлял над этой проблемой, особенно когда попробовал свои силы в TDD.
Я натолкнулся на два сообщения, которые, я думаю, достаточно подробно решают эту проблему в случае TDD.
В итоге:
При использовании методов разработки (проектирования) на основе тестирования частные методы должны возникать только в процессе перефакторинга уже работающего и протестированного кода.
По самой природе процесса любая часть простой функциональности реализации, извлеченная из тщательно протестированной функции, будет подвергаться самопроверке (т. Е. Охват косвенным тестированием).
Мне кажется достаточно ясным, что в начальной части кодирования большинство методов будут функциями более высокого уровня, потому что они инкапсулируют / описывают дизайн.
Поэтому эти методы будут общедоступными, и их тестирование будет достаточно простым.
Приватные методы появятся позже, когда все будет работать хорошо, и мы учитываем факторы для удобства чтения и чистоты .
источник
Как указано выше: «Если вы не тестируете свои личные методы, откуда вы знаете, что они не сломаются?»
Это серьезная проблема. Одна из главных задач модульных тестов - знать, где, когда и как что-то сломалось как можно скорее. Таким образом, уменьшается значительный объем усилий по разработке и обеспечению качества. Если все, что проверяется, это публика, то у вас нет честного освещения и определения внутренних элементов класса.
Я нашел один из лучших способов сделать это - просто добавить ссылку на тест в проект и поместить тесты в класс, параллельный частным методам. Вставьте соответствующую логику сборки, чтобы тесты не встраивались в окончательный проект.
Тогда у вас есть все преимущества тестирования этих методов, и вы можете найти проблемы в считанные секунды, а не минуты или часы.
Итак, в заключение, да, юнит-тестирование ваших частных методов.
источник
Вы не должны . Если ваши частные методы имеют достаточную сложность, которую необходимо протестировать, вы должны поместить их в другой класс. Сохраняйте высокую сплоченность , у класса должна быть только одна цель. Класс public интерфейса должен быть достаточно.
источник
Если вы не тестируете свои личные методы, как вы узнаете, что они не сломаются?
источник
Это очевидно зависит от языка. В прошлом с c ++ я объявил класс тестирования своим классом. К сожалению, для этого требуется, чтобы ваш производственный код знал о классе тестирования.
источник
Я понимаю точку зрения, когда частные методы рассматриваются как детали реализации, и тогда не нужно тестировать. И я бы придерживался этого правила, если бы нам пришлось развиваться только вне объекта. Но мы, мы какие-то ограниченные разработчики, которые разрабатывают только вне объектов, вызывая только свои публичные методы? Или мы на самом деле также развиваем этот объект? Поскольку мы не обязаны программировать внешние объекты, нам, вероятно, придется вызывать эти частные методы в новые открытые методы, которые мы разрабатываем. Разве не было бы замечательно знать, что закрытый метод противостоит всем шансам?
Я знаю, что некоторые люди могут ответить, что если мы разрабатываем другой публичный метод для этого объекта, то этот должен быть протестирован и все (частный метод может продолжать жить без теста). Но это также верно для любых открытых методов объекта: при разработке веб-приложения все открытые методы объекта вызываются из методов контроллеров и, следовательно, могут рассматриваться как детали реализации для контроллеров.
Так почему же мы тестируем объекты? Поскольку это действительно трудно, нельзя сказать невозможно, быть уверенным, что мы тестируем методы контроллеров с соответствующим вводом, который будет запускать все ветви базового кода. Другими словами, чем выше мы в стеке, тем сложнее проверить все поведение. И то же самое для частных методов.
Для меня граница между частными и публичными методами является психологическим критерием, когда дело доходит до тестов. Критерии, которые имеют большее значение для меня:
источник
Если я нахожу, что закрытый метод является огромным, сложным или достаточно важным, чтобы требовать собственных тестов, я просто помещаю его в другой класс и делаю его общедоступным (метод Object). Затем я могу легко протестировать ранее закрытый, но теперь публичный метод, который теперь живет в своем собственном классе.
источник
Я никогда не понимал концепцию модульного тестирования, но теперь я знаю, какова его цель.
Модульный тест не является полным тестом . Таким образом, это не замена для QA и ручного тестирования. Концепция TDD в этом аспекте неверна, поскольку вы не можете протестировать все, включая частные методы, но также и методы, которые используют ресурсы (особенно ресурсы, которые мы не контролируем). TDD базируется на всем своем качестве, это то, чего нельзя было достичь.
Юнит-тест - это скорее сводный тест. Вы помечаете произвольный пивот, и результат его должен остаться прежним.
источник
Публичное и частное не является полезным отличием того, что apis вызывать из ваших тестов, равно как и метод против класса. Большинство тестируемых модулей видны в одном контексте, но скрыты в других.
Важен охват и стоимость. Вам необходимо минимизировать затраты при достижении целей покрытия вашего проекта (линия, ветвь, путь, блок, метод, класс, класс эквивалентности, сценарий использования ... все, что решит команда).
Поэтому используйте инструменты для обеспечения покрытия и разрабатывайте свои тесты так, чтобы они приносили наименьшие затраты (краткосрочные и долгосрочные )
Не делайте тесты дороже, чем необходимо. Если это самое дешевое, то проверять только общедоступные точки входа. Если это самый дешевый способ тестирования частных методов, сделайте это.
По мере того, как вы становитесь более опытным, вы будете лучше предсказывать, когда стоит проводить рефакторинг, чтобы избежать долгосрочных затрат на обслуживание тестов.
источник
Если метод достаточно значительный / достаточно сложный, я обычно делаю его «защищенным» и проверяю его. Некоторые методы будут оставлены закрытыми и неявно проверены как часть модульных тестов для открытых / защищенных методов.
источник
Я вижу, что многие люди придерживаются единого мнения: тестирование на общественном уровне. но разве это не то, что делает наша команда QA? Они проверяют ввод и ожидаемый результат. Если как разработчики мы тестируем только общедоступные методы, то мы просто переделываем работу QA и не добавляем никакой ценности путем «модульного тестирования».
источник
Ответ на вопрос «Должен ли я проверить частные методы?» это "....... иногда". Обычно вы должны проверять интерфейс ваших классов.
Вот пример:
В
RefactoredThing
вас теперь есть 5 тестов, 2 из которых вы должны были обновить рефакторинга, но его функциональность объекта действительно не изменилось. Итак, допустим, что все более сложно, и у вас есть метод, который определяет порядок вывода, такой как:Это не должно быть выполнено внешним пользователем, но ваш инкапсулирующий класс может быть слишком тяжелым, чтобы проходить через него столько логики снова и снова. В этом случае, возможно, вы бы предпочли извлечь это в отдельный класс, дать этому классу интерфейс и протестировать его.
И, наконец, допустим, что ваш основной объект очень тяжелый, а метод довольно маленький, и вам действительно нужно убедиться, что вывод правильный. Вы думаете: «Я должен проверить этот частный метод!». Возможно, вы можете сделать свой объект легче, передавая некоторые тяжелые работы в качестве параметра инициализации? Тогда вы можете передать что-то более легкое и протестировать против этого.
источник
Нет. Вы не должны проверять частные методы, почему? и, кроме того, популярная среда моделирования, такая как Mockito, не обеспечивает поддержку тестирования частных методов.
источник
Одним из основных моментов является
Если мы проверяем правильность логики, а частный метод несет логику, мы должны проверить ее. Не так ли? Так почему мы собираемся пропустить это?
Написание тестов, основанных на видимости методов, является совершенно неактуальной идеей.
Наоборот
С другой стороны, главная проблема заключается в вызове частного метода вне исходного класса. Кроме того, существуют ограничения для насмешки частного метода в некоторых инструментах насмешек. (Пример: Мокито )
Хотя есть некоторые инструменты, такие как Power Mock, которые поддерживают это, это опасная операция. Причина в том, что для этого нужно взломать JVM.
Можно обойти эту проблему (если вы хотите написать контрольные примеры для частных методов)
Объявите эти частные методы как защищенные . Но это может быть не удобно для нескольких ситуаций.
источник
Речь идет не только о публичных или частных методах или функциях, но и о деталях реализации. Частные функции - это только один аспект деталей реализации.
В конце концов, юнит-тестирование - это подход белого тестирования. Например, тот, кто использует анализ покрытия для определения частей кода, которыми до сих пор пренебрегали при тестировании, углубляется в детали реализации.
А) Да, вы должны тестировать детали реализации:
Представьте себе функцию сортировки, которая по соображениям производительности использует частную реализацию BubbleSort, если имеется до 10 элементов, и частную реализацию другого подхода к сортировке (скажем, heapsort), если имеется более 10 элементов. Публичный API - это функция сортировки. Однако ваш набор тестов лучше использует знания о том, что на самом деле используются два алгоритма сортировки.
В этом примере, безусловно, вы можете выполнить тесты с открытым API. Это, однако, потребует наличия нескольких тестовых случаев, которые выполняют функцию сортировки с более чем 10 элементами, так что алгоритм heapsort достаточно хорошо протестирован. Наличие таких тестовых примеров само по себе указывает на то, что набор тестов связан с деталями реализации функции.
Если детали реализации функции сортировки изменятся, возможно, из-за того, что предел между двумя алгоритмами сортировки будет смещен, или что heapsort будет заменен на mergesort или что-то еще: существующие тесты будут продолжать работать. Тем не менее, их ценность сомнительна, и их, вероятно, необходимо переработать, чтобы лучше протестировать измененную функцию сортировки. Другими словами, будут предприняты усилия по обслуживанию, несмотря на то, что тесты проводились на общедоступном API.
Б) Как проверить детали реализации
Одна из причин, почему многие люди утверждают, что не следует тестировать частные функции или детали реализации, состоит в том, что детали реализации с большей вероятностью изменятся. Эта более высокая вероятность изменения, по крайней мере, является одной из причин скрытия деталей реализации за интерфейсами.
Теперь предположим, что реализация интерфейса содержит более крупные частные части, для которых могут быть предусмотрены отдельные тесты внутреннего интерфейса. Некоторые люди утверждают, что эти части не должны быть проверены в частном порядке, они должны быть превращены в нечто публичное. После публичного юнит-тестирования этот код будет в порядке.
Это интересно: хотя интерфейс был внутренним, он, скорее всего, изменится, что является деталью реализации. Взяв тот же интерфейс, сделав его общедоступным, произойдет некое волшебное преобразование, а именно превращение его в интерфейс, который с меньшей вероятностью изменится. Очевидно, в этой аргументации есть некоторый недостаток.
Но, тем не менее, за этим стоит некоторая правда: при тестировании деталей реализации, в частности с использованием внутренних интерфейсов, следует стремиться использовать интерфейсы, которые, вероятно, останутся стабильными. Однако вероятность того, что какой-либо интерфейс будет стабильным, не просто определяется в зависимости от того, является ли он открытым или закрытым. В проектах из мира, в которых я работал в течение некоторого времени, публичные интерфейсы также достаточно часто меняются, и многие частные интерфейсы остаются неизменными целую вечность.
Тем не менее, рекомендуется использовать «переднюю дверь первым» (см. Http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Но имейте в виду, что он называется «сначала входная дверь», а не «только входная дверь».
C) Резюме
Проверьте также детали реализации. Предпочитают тестирование на стабильных интерфейсах (публичных или приватных). Если детали реализации меняются, также необходимо пересмотреть тесты общедоступного API. Превращение чего-то частного в публичное не может волшебным образом изменить его стабильность.
источник
Да, вы должны проверить частные методы, где это возможно. Зачем? Чтобы избежать ненужного взрыва в пространстве состояний тестовых случаев, которые в конечном итоге просто неявно тестируют одни и те же частные функции повторно на одних и тех же входах. Давайте объясним почему на примере.
Рассмотрим следующий слегка надуманный пример. Предположим, мы хотим публично представить функцию, которая принимает 3 целых числа и возвращает истину тогда и только тогда, когда все эти 3 целых числа простые. Мы могли бы реализовать это так:
Теперь, если бы мы придерживались строгого подхода, согласно которому должны проверяться только публичные функции, нам разрешалось бы только проверять,
allPrime
а неisPrime
илиandAll
.В качестве тестера, мы могли бы быть заинтересованы в пять возможностей для каждого аргумента:
< 0
,= 0
,= 1
,prime > 1
,not prime > 1
. Но чтобы быть тщательным, мы должны также увидеть, как каждая комбинация аргументов играет вместе. Так что это5*5*5
= 125 тестовых случаев, нам нужно было бы тщательно протестировать эту функцию, согласно нашей интуиции.С другой стороны, если бы нам было разрешено тестировать частные функции, мы могли бы охватить как можно больше с меньшим количеством тестовых случаев. Нам понадобится всего 5 тестовых случаев, чтобы протестировать
isPrime
до того же уровня, что и наша предыдущая интуиция И в соответствии с гипотезой о малом объеме, предложенной Дэниелом Джексоном, нам нужно было бы протестироватьandAll
функцию только до небольшой длины, например 3 или 4. Это будет не более 16 тестов. Итого 21 тест. Вместо 125. Конечно, мы, вероятно, хотели бы провести несколько тестовallPrime
, но мы бы не чувствовали себя настолько обязанными, чтобы охватить все 125 комбинаций входных сценариев, о которых мы говорили, о которых мы заботились. Всего несколько счастливых путей.Придуманный пример, конечно, но это было необходимо для наглядной демонстрации. И шаблон распространяется на реальное программное обеспечение. Частные функции, как правило, являются строительными блоками самого низкого уровня и, таким образом, часто объединяются для получения логики более высокого уровня. То есть на более высоких уровнях у нас больше повторений вещей более низкого уровня из-за различных комбинаций.
источник
isPrime
действительно независимы, поэтому вслепую тестировать каждую комбинацию довольно бессмысленно. Во-вторых, маркировка чистой функции с именемisPrime
private нарушает так много правил проектирования, что я даже не знаю, с чего начать.isPrime
должно очень четко быть публичной функцией. При этом я понимаю, что вы говорите, несмотря на этот крайне плохой пример. Однако он основан на предпосылке, что вы хотите провести комбинированное тестирование, когда в реальных программных системах это редко хорошая идея.Вы также можете сделать свой метод package-private, то есть по умолчанию, и вы сможете использовать его для модульного тестирования, если только он не требуется для приватности.
источник