unit-testing - примеры - юнит тесты си




Должны ли частные/защищенные методы проходить единичный тест? (8)

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

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

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

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

Мои мысли:

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

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

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


Вы написали:

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

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

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

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

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

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

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


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

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

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


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

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


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

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


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

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

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


Существует две причины для написания тестов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Да и нет.

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

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

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


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

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

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

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

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

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

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

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

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


Я согласен со всеми: ответ на ваш вопрос «нет».

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

Я также хотел бы добавить, что вопрос (и ответ «нет») также применим к общедоступным методам, которые вы можете ввести в классы.

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

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





tdd