Должны ли частные / защищенные методы проходить модульное тестирование?

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

Теперь мой вопрос о частных и защищенных методах, которые мне, возможно, придется написать в своем классе для поддержки методов / свойств, предоставляемых интерфейсом:

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

  • Должны ли защищенные методы в классе иметь свои собственные модульные тесты?

Мои мысли:

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

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

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

13 ответов

Решение

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

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

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

Я не согласен с большинством постеров.

Наиболее важным правилом является: РАБОЧИЙ КОД ТРУБЫ ТЕОРЕТИЧЕСКИЕ ПРАВИЛА о публичных / защищенных / частных

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

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

Есть замечательная история о психологе, который дал детям тест. Он дал каждому ребенку две деревянные доски с веревкой, прикрепленной к каждому концу, и попросил их пересечь комнату, не касаясь ногами пола, как можно быстрее. Все дети использовали доски, как маленькие лыжи, по одной ноге на каждую доску, держа их за веревки и скользя по полу. Затем он дал им ту же задачу, но используя только одну доску. Они поворачивались /"шли" по полу, по одной ноге на каждом конце доски - и они были БЫСТРЕЕ!

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

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

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

Но в конце дня подумайте о том, сколько ошибок было вызвано отсутствием тестирования. Затем сравните, сколько ошибок было вызвано "слишком видимыми" методами. Этот ответ должен повлиять на ваше решение.

Вы написали:

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

Пожалуйста, позвольте мне перефразировать это на языке BDD:

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

* Может быть актуальным Interface или просто доступный API класса, например: Ruby не имеет интерфейсов.

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

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

Надеюсь это поможет!

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

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

Нет! Только тестовые интерфейсы.

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

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

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

Есть две причины для написания тестов:

  1. Утверждение ожидаемого поведения
  2. Предотвращение регресса поведения

Взятие на (1) Утверждение ожидаемого поведения:

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

  • Разве то, что я только что написал, работает?
  • Этот цикл действительно заканчивается?
  • Это цикл в порядке, я думаю, что это?
  • Будет ли это работать для нулевого ввода?

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

Это хорошая идея делать это там, где вы чувствуете, что это необходимо. Любой код, который немного сложно понять, или нетривиален. Даже тривиальный код может извлечь из этого пользу. Все дело в твоей уверенности. Как часто это делать и как далеко идти, будет зависеть от вашего собственного удовлетворения. Остановитесь, когда сможете с уверенностью ответить Да на: Вы уверены, что это работает?

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

Взятие на (2) Предотвращение регрессии поведения:

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

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

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

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

Это ваш типичный модульный тест, о котором все чаще всего говорят, когда упоминают TDD или BDD. Дело в том, чтобы укрепить границы и защитить их от изменений. Вы не хотите проверять закрытые методы для этого, потому что закрытый метод не является границей. Защищенные методы - это ограниченная граница, и я бы их защитил. Они не выставлены миру, но все еще выставлены другим отделениям или "Единицам".

Что с этим делать?

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

И да и нет.

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

Наконец: напишите тесты для ваших границ. Проведите юнит-тест для каждой точки, которая будет использоваться другими блоками вашей системы. Убедитесь, что этот тест подтверждает семантический контракт, имя метода, количество аргументов и т. Д. А также убедитесь, что тест подтверждает доступное поведение модуля. Ваш тест должен продемонстрировать, как использовать устройство и что оно может делать. Держите эти тесты включенными, чтобы они выполнялись при каждом нажатии кода.

ПРИМЕЧАНИЕ. Причина, по которой вы отключили первый набор тестов, состоит в том, чтобы разрешить проведение рефакторинга. Активный тест - это кодовая связь. Это предотвращает будущие изменения кода, который он тестирует. Вы хотите это только для своих интерфейсов и контрактов на взаимодействие.

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

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

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

"Защищенный" является своего рода другим предметом обсуждения, но в целом его вообще не должно быть. Либо он нарушает инкапсуляцию развернутого кода, либо вынуждает вас наследовать от этого класса, просто для его модульного тестирования, даже иногда вам не нужно наследовать.

Простое сокрытие метода для клиента (создание приватного) не дает ему права не проверяться. Следовательно, они могут быть проверены общедоступными методами, как упоминалось ранее.

Я тоже согласен с ответом @kwbeam о том, что не нужно тестировать частные методы. Однако важно отметить, что защищенные методы являются частью экспортированного API класса и, следовательно, ДОЛЖНЫ быть протестированы.

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

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

Надеюсь, поможет!

Я согласен со всеми остальными: ответ на ваш вопрос "нет".

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

Я также добавил бы, что вопрос (и ответ "нет") также относится к открытым методам, которые вы можете представить классам.

  • Если вы добавляете методы (общедоступные / защищенные или частные), потому что они проходят неудачный тест, то вы более или менее достигли цели TDD.
  • Если вы добавляете методы (общедоступные / защищенные или частные), потому что вы просто решаете нарушить TDD, тогда ваш охват кода должен их уловить, и вы сможете улучшить свой процесс.

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

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

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

Простой ответ - НЕТ.

Пояснение: зачем тестировать приватную функцию? В любом случае он будет автоматически протестирован (и должен быть протестирован), когда вы протестируете функцию / метод, который его использует - частную функцию.

Другие вопросы по тегам