При разработке TDD первое, что вы обычно делаете, - это создаете свой интерфейс, а затем начинаете писать свои модульные тесты для этого интерфейса. По мере прохождения процесса TDD вы в конечном итоге создаете класс, реализующий интерфейс, и затем в какой-то момент ваш модульный тест будет пройден.
Теперь мой вопрос касается частных и защищенных методов, которые мне, возможно, придется написать в моем классе для поддержки методов / свойств, предоставляемых интерфейсом:
Должны ли частные методы в классе иметь свои собственные модульные тесты?
Должны ли защищенные методы в классе иметь свои собственные модульные тесты?
Мои мысли:
Тем более, что я кодирую интерфейсы, я не должен беспокоиться о защищенных / частных методах, поскольку они являются черными ящиками.
Поскольку я использую интерфейсы, я пишу модульные тесты для проверки того, что определенный контракт должным образом реализован различными классами, реализующими интерфейс, поэтому я снова не должен беспокоиться о частных / защищенных методах, и они должны выполняться с помощью модульных тестов, которые вызывают методы / свойства, определенные интерфейсом.
Если мое покрытие кода не показывает, что используются защищенные / частные методы, значит, у меня нет нужных модульных тестов или у меня есть код, который не используется и должен быть удален.
источник
Ответы:
Нет, я не думаю о тестировании частных или защищенных методов. Частные и защищенные методы класса не являются частью общедоступного интерфейса, поэтому они не раскрывают общедоступное поведение. Обычно эти методы создаются путем рефакторинга, который вы применяете после того, как тест стал зеленым.
Итак, эти частные методы тестируются неявно тестами, которые подтверждают поведение вашего публичного интерфейса.
В более философском плане помните, что вы тестируете поведение, а не методы. Итак, если вы думаете о наборе вещей, которые может выполнять тестируемый класс, если вы можете протестировать и утверждать, что класс ведет себя так, как ожидалось, существуют ли частные (и защищенные) методы, которые используются внутри класса для реализации такое поведение не имеет значения. Эти методы являются деталями реализации публичного поведения.
источник
Я не согласен с большинством плакатов.
Самое важное правило: РАБОЧИЙ КОД ПРЕПЯТСТВУЕТ ТЕОРЕТИЧЕСКИМ ПРАВИЛАМ о публичном / защищенном / частном.
Ваш код должен быть тщательно протестирован. Если вы можете достичь этого, написав тесты для общедоступных методов, которые в достаточной степени используют защищенные / частные методы, это прекрасно.
Если вы не можете, то либо проведите рефакторинг, чтобы вы могли, либо измените защищенные / частные правила.
Есть отличная история о психологе, который проводил детям тест. Он дал каждому ребенку две деревянные доски с веревкой, прикрепленной к каждому концу, и попросил их пересечь комнату, не касаясь ногами пола, как можно быстрее. Все дети использовали доски как маленькие лыжи, ставя одну ногу на каждую доску, держа их за веревки и скользя по полу. Затем он дал им такое же задание, но с использованием только ОДНОЙ доски. Они поворачивались / «шли» по полу, по одной ноге на каждом конце единой доски - и они были БЫСТРЕЕ!
Тот факт, что Java (или любой другой язык) имеет функцию (частную / защищенную / общедоступную), не обязательно означает, что вы пишете лучший код, потому что вы его используете!
Теперь всегда будут способы оптимизировать / минимизировать этот конфликт. На большинстве языков вы можете сделать метод защищенным (вместо общедоступного) и поместить тестовый класс в тот же пакет (или что-то еще), и метод будет доступен для тестирования. Есть аннотации, которые могут помочь, как описано в других плакатах. Вы можете использовать отражение, чтобы получить доступ к частным методам (фу).
Контекст также имеет значение. Если вы пишете API для использования посторонними людьми, важнее общедоступный / частный. Если это внутренний проект - кого это волнует?
Но в конце концов подумайте о том, сколько ошибок было вызвано отсутствием тестирования. Затем сравните с тем, сколько ошибок было вызвано «слишком заметными» методами. Этот ответ должен повлиять на ваше решение.
источник
Вы написали:
Пожалуйста, позвольте мне перефразировать это на языке BDD :
Вот почему вы не тестируете частные методы - потому что тест - это пример того, как использовать класс, и вы не можете их использовать. Что-то, что вы можете сделать, если хотите, - это делегировать обязанности в частных методах сотрудничающему классу, а затем имитировать / заглушить этот помощник.
Используя защищенные методы, вы говорите, что класс, расширяющий ваш класс, должен иметь определенное поведение и предоставлять некоторую ценность. Затем вы можете использовать расширения своего класса, чтобы продемонстрировать это поведение. Например, если вы пишете класс упорядоченной коллекции, вы можете продемонстрировать, что два расширения с одинаковым содержимым демонстрируют равенство.
Надеюсь это поможет!
источник
Когда вы пишете модульные тесты для своего класса, вам не обязательно заботиться о том, реализована ли функциональность класса непосредственно в методе в общедоступном интерфейсе или реализована в серии частных методов. Итак, да, вы должны тестировать свои частные методы, но вам не нужно для этого вызывать их напрямую из тестового кода (прямое тестирование частных методов тесно связывает вашу реализацию с вашими тестами и делает рефакторинг излишне сложным).
Защищенные методы формируют другой контракт между вашим классом и его будущими потомками, поэтому вам действительно следует тестировать его в той же степени, что и ваш публичный интерфейс, чтобы убедиться, что контракт четко определен и выполняется.
источник
Нет! Только тестовые интерфейсы.
Одним из больших преимуществ TDD является гарантия того, что интерфейс работает независимо от того, как вы решили реализовать частные методы.
источник
Завершая то, что другие говорили выше, я бы сказал, что защищенные методы являются частью какого-то интерфейса: это просто тот, который подвергается наследованию, а не композиции, о чем все склонны думать при рассмотрении интерфейсов.
Пометка метода как защищенного вместо частного означает, что ожидается, что он будет использоваться сторонним кодом, поэтому необходимо определить и протестировать какой-то контракт, как это происходит с обычными интерфейсами, определенными общедоступными методами, которые открыты как для наследования, так и для композиции .
источник
Есть две причины для написания тестов:
Подход к (1) утверждению ожидаемого поведения:
Когда вы утверждаете ожидаемое поведение, вы хотите убедиться, что код работает так, как вы думаете. По сути, это автоматизированный способ выполнения рутинной ручной проверки, которую может выполнить любой разработчик при реализации любого вида кода:
На эти вопросы мы все отвечаем в уме, и обычно мы пытаемся выполнить код и в голове, чтобы убедиться, что он работает. В таких случаях часто бывает полезно, чтобы компьютер ответил на них определенным образом. Итак, мы пишем модульный тест, который утверждает, что это так. Это дает нам уверенность в нашем коде, помогает нам находить дефекты на раннем этапе и даже может помочь в реализации кода.
Это хорошая идея - делать это там, где вы считаете необходимым. Любой код, который немного сложен для понимания или нетривиален. От этого может выиграть даже тривиальный код. Все дело в вашей уверенности. Как часто это делать и как далеко идти, будет зависеть от вашего собственного удовлетворения. Остановитесь, когда сможете уверенно ответить «да» на вопрос: «Вы уверены, что это работает?»
Для этого вида тестирования вам не важны видимость, интерфейсы или что-то еще, вас волнует только рабочий код. Итак, да, вы бы протестировали частные и защищенные методы, если бы почувствовали, что их нужно протестировать, чтобы вы ответили «Да» на вопрос.
Подход (2) Предотвращение регресса поведения:
После того, как у вас есть рабочий код, вам понадобится механизм для защиты этого кода от будущих повреждений. Если бы никто и никогда больше не касался вашего источника и вашей конфигурации, вам бы это не понадобилось, но в большинстве случаев вы или другие будут касаться источника и конфигураций вашего программного обеспечения. Эта внутренняя возня с большой вероятностью нарушит ваш рабочий код.
В большинстве языков уже существуют механизмы защиты от этого ущерба. Функции видимости - это один механизм. Частный метод изолирован и скрыт. Инкапсуляция - это еще один механизм, при котором вы разделяете вещи на части, чтобы изменение других частей не влияло на другие.
Общий механизм для этого называется: кодирование до границы. Создавая границы между частями кода, вы защищаете все внутри границы от вещей за ее пределами. Границы становятся точкой взаимодействия и контрактом, по которому взаимодействуют вещи.
Это означает, что изменения границы, либо путем нарушения интерфейса, либо нарушения его ожидаемого поведения, могут повредить и, возможно, нарушить другие границы, которые от него зависят. Вот почему рекомендуется иметь модульный тест, нацеленный на эти границы и утверждающий, что они не меняются семантически и в поведении.
Это ваш типичный модульный тест, о котором все говорят, когда упоминают TDD или BDD. Дело в том, чтобы ужесточить границы и защитить их от изменений. Вы не хотите тестировать частные методы для этого, потому что частный метод не является границей. Защищенные методы - это ограниченная граница, и я бы защищал их. Они не открыты миру, но все же подвержены воздействию других отсеков или «Единиц».
Что с этим делать?
Как мы видели, есть веская причина для модульного тестирования общедоступных и защищенных методов, чтобы утверждать, что наши интерфейсы не меняются. Также есть веская причина протестировать частные методы, чтобы убедиться, что наша реализация работает. Так следует ли нам проводить модульное тестирование их всех?
Да и нет.
Во-первых : протестируйте все методы, которые, по вашему мнению, необходимы для окончательного доказательства того, что они работают в большинстве случаев, чтобы быть уверенным в том, что ваш код работает независимо от видимости. Затем отключите эти тесты. Они сделали там работу.
Наконец : напишите тесты для ваших границ. Проведите модульный тест для каждой точки, которая будет использоваться другими модулями вашей системы. Убедитесь, что этот тест подтверждает семантический контракт, имя метода, количество аргументов и т. Д. А также убедитесь, что тест подтверждает доступное поведение модуля. Ваш тест должен продемонстрировать, как использовать устройство и на что он способен. Оставьте эти тесты включенными, чтобы они запускались при каждом нажатии кода.
ПРИМЕЧАНИЕ. Причина, по которой вы отключили первый набор тестов, состоит в том, чтобы позволить провести рефакторинг. Активный тест - это комбинация кода. Это предотвращает будущую модификацию кода, который он тестирует. Это нужно только для ваших интерфейсов и контрактов на взаимодействие.
источник
Нет, вам не следует тестировать частные методы (как бы вы все равно не использовали что-то ужасное, например, отражение). С защищенными методами это немного менее очевидно, в C # вы можете сделать что-то защищенным внутренним, и я думаю, что это нормально, чтобы протестировать производные классы, которые реализуют всю свою функциональность с помощью методов шаблона шаблона.
Но в целом, если вы думаете, что ваши общедоступные методы делают слишком много, пора реорганизовать ваши классы в более атомарные классы, а затем протестировать эти классы.
источник
Я тоже согласен с ответом @kwbeam о том, что не тестирует частные методы. Тем не менее, я хотел бы выделить важный момент - защищенные методы ЯВЛЯЮТСЯ частью экспортируемого API класса и, следовательно, ДОЛЖНЫ быть протестированы.
Защищенные методы могут быть недоступны для всех, но вы определенно предоставляете подклассам возможность использовать / переопределять их. Что-то вне класса может получить к ним доступ, и, следовательно, вам необходимо убедиться, что эти защищенные члены ведут себя ожидаемым образом. Поэтому не тестируйте частные методы, а тестируйте общедоступные и защищенные методы.
Если вы считаете, что у вас есть частный метод, содержащий критическую логику, я бы попытался выделить его в отдельный объект, изолировать и предоставить способ проверить его поведение.
Надеюсь, поможет!
источник
Если вы стремитесь к высокому покрытию кода (я предлагаю вам это сделать), вам следует протестировать все свои методы, независимо от того, являются ли они частными или защищенными.
Защищенный - это своего рода отдельная тема для обсуждения, но в целом ее там вообще не должно быть. Либо он нарушает инкапсуляцию в развернутом коде, либо заставляет нас наследовать от этого класса, просто для его модульного тестирования, даже иногда вам не нужно наследовать.
Простое сокрытие метода для клиента (создание закрытого) не дает ему права не подвергаться аудиту. Следовательно, они могут быть протестированы общедоступными методами, как упоминалось ранее.
источник
Я согласен со всеми: ответ на ваш вопрос - «нет».
Действительно, вы совершенно правы в своем подходе и своих мыслях, особенно в отношении покрытия кода.
Я бы также добавил, что этот вопрос (и ответ «нет») также относится к общедоступным методам, которые вы можете ввести в классы.
Кроме того, для C ++ (а я должен думать только для C ++) я реализую интерфейсы, используя только частные методы, чтобы указать, что класс должен использоваться только через интерфейс, который он реализует. Это мешает мне ошибочно вызывать новые методы, добавленные в мою реализацию из моих тестов.
источник
Хороший дизайн означает разделение приложения на несколько тестируемых модулей. После этого некоторые модули становятся доступными общедоступному API, а некоторые другие могут быть недоступны. Кроме того, точки взаимодействия между открытыми модулями и этими «внутренними» модулями также не являются частью публичного API.
Я думаю, что как только у нас будет идентифицируемый модуль, он выиграет от модульных тестов, независимо от того, доступен ли он через общедоступный API или нет.
источник