TDD Red-Green-Refactor и если / как проверить методы, которые становятся частными

91

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

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

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

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

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

Итак, я получаю приватные методы и набор тестов, которые его проверяют. Так как многие люди советуют не проверять частные методы, я чувствую, что я загнал себя в угол. Но где именно я потерпел неудачу?

Я полагаю, что я мог бы начать с более высокого уровня, написав тест, который проверяет то, что в конечном итоге станет моим публичным методом (то есть findAndFilterOutAllNonNumericColumns), но это несколько противоречит цели TDD (по крайней мере, согласно дяде Бобу) : Вы должны постоянно переключаться между написанием тестов и рабочим кодом, и что в любой момент времени все ваши тесты работали в последнюю минуту или около того. Потому что, если я начну с написания теста для общедоступного метода, у меня будет несколько минут (или часов, или даже дней в очень сложных случаях), прежде чем я получу все детали в частных методах для работы, чтобы тестирование общедоступного метода метод проходит.

Так что делать? Является ли TDD (с быстрым циклом красный-зеленый-рефактор) просто несовместимым с частными методами? Или есть ошибка в моем дизайне?

Хенрик Берг
источник
2
Либо эти две части функциональности достаточно различны, чтобы быть разными единицами - в этом случае частные методы, вероятно, должны быть в своих собственных классах - или это одна и та же единица, и в этом случае я не понимаю, почему вы пишете тесты для внутреннее поведение устройства. Что касается предпоследнего абзаца, я не вижу конфликта. Зачем вам нужно написать целый сложный приватный метод, чтобы пройти один тест? Почему бы не вытолкнуть его постепенно с помощью публичного метода или начать с него, а затем извлечь его?
Бен Ааронсон
26
Почему люди воспринимают идиомы и клише из книг по программированию и блогов, как фактические рекомендации о том, как программировать, мне не под силу.
AK_
7
Мне не нравится TDD именно по этой причине: если вы находитесь в новой области, тогда вы будете выполнять много дополнительной работы, пытаясь выяснить, какой должна быть архитектура и как работают определенные вещи. С другой стороны: если вы находитесь в той области, с которой уже сталкивались, тогда будет полезно сначала написать тесты, а не раздражать вас, потому что intellisense не понимает, почему вы пишете не компилируемый код. Я гораздо больше люблю думать о дизайне, писать его, а затем тестировать его.
Йерун Ванневел
1
«Большинство людей, похоже, согласны с тем, что частные методы не следует тестировать напрямую» - нет, тестируйте метод напрямую, если это имеет смысл. Скройте это, как privateбудто это имеет смысл сделать так.
OSA

Ответы:

44

Единицы

Я думаю, что могу точно определить, где проблема началась:

Я подумал, мне понадобится метод, который находит все нечисловые поля в строке.

За этим следует немедленно спросить себя: «Будет ли это отдельный тестируемый модуль gatherNonNumericColumnsили его часть?»

Если ответ « да, отдельный », то ваш порядок действий прост: этот метод должен быть общедоступным в соответствующем классе, чтобы его можно было проверить как единое целое. Ваш менталитет что-то вроде: «Мне нужно протестировать один метод, и мне также нужно проверить другой метод»

Из того, что вы говорите, вы поняли, что ответ « нет, часть того же ». На этом этапе ваш план больше не должен полностью писать и тестировать, а findNonNumericFields затем писать gatherNonNumericColumns. Вместо этого следует просто написать gatherNonNumericColumns. На данный момент, это findNonNumericFieldsдолжно быть лишь вероятной частью пункта назначения, который вы имели в виду, выбирая следующий красный контрольный пример и проводя рефакторинг. На этот раз ваш менталитет звучит так: «Мне нужно протестировать один метод, и пока я делаю это, я должен помнить, что моя законченная реализация, вероятно, будет включать этот другой метод».


Держать короткий цикл

Выполнение вышеизложенного не должно привести к проблемам, которые вы описываете в своем предпоследнем абзаце:

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

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


Дорожная карта

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

Бен Ааронсон
источник
2
Я думаю, что это ответ прямо здесь. Принимая TDD в ООП среде, я часто испытывал трудности с преодолением своих собственных инстинктов снизу вверх. Да, функции должны быть небольшими, но это после рефакторинга. Раньше они могли быть огромными монолитами. +1
Жоао Мендес
2
@ JoãoMendes Ну, я не уверен, что вы должны прийти в состояние огромного монолита перед рефакторингом, особенно на очень коротких циклах RGR. Но да, в тестируемом блоке работа снизу вверх может привести к проблемам, которые описывает OP.
Бен Ааронсон
1
Хорошо, я думаю, я понимаю, где это пошло не так сейчас. Большое спасибо всем вам (пометили это как ответ, но большинство других ответов одинаково полезны)
Хенрик Берг
66

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

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

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

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

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

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

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

gbjbaanb
источник
14
Мне кажется, что этот ответ полностью игнорирует подход TDD в вопросе ОП. Это просто повторение «не испытывают частные методы» мантры, но это не объясняет , как TDD - что это на самом деле метод , основанный - может работать с не-метода единицы на основе тестового подхода.
Док Браун
6
@DocBrown нет, он отвечает ему полностью, говоря: «не чрезмерно гранулируйте» свои подразделения и усложняйте себе жизнь. TDD основывается не на методах, а на единицах, где единица имеет смысл. Если у вас есть библиотека C, то да, каждая единица будет функцией. Если у вас есть класс, юнит - это объект. Как говорит Фаулер, иногда единица - это несколько тесно связанных классов. Я думаю, что многие люди считают, что модульное тестирование является методом по методу просто потому, что некоторые тупые инструменты генерируют заглушки на основе методов.
gbjbaanb
3
@gbjbaanb: попытаться предложить тест, который позволит ОП реализовать свой «сбор нечисловых полей в строке», сначала используя чистый TDD, не имея открытого интерфейса класса, который он намеревается написать уже написанным.
Док Браун
8
Я должен согласиться с @DocBrown здесь. Проблема спрашивающего не в том, что он желает получить более детальную тестируемость, чем он может достичь, не тестируя частные методы. Это то, что он пытался следовать строгому подходу TDD и - без планирования как такового - что привело его к ударе о стену, где он внезапно обнаружил, что у него есть куча тестов для того, что должно быть частным методом. Этот ответ не поможет с этим. Это хороший ответ на какой-то вопрос, только не этот.
Бен Ааронсон
7
@ Мэтью: его ошибка в том, что он написал функцию в первую очередь. В идеале, он должен был написать открытый метод как спагетти-код, а затем преобразовать его в приватную функцию в цикле рефакторинга - не отмечать его как приватный в цикле рефакторинга.
Slebetman
51

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

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

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

Килиан Фот
источник
Да, я согласен с вами. Но у меня есть проблема с вашим первым утверждением, как с «достаточно сложной», так и с «достаточно отдельной» частью. Относительно «достаточно сложного»: я пытаюсь выполнить быстрый красно-зеленый цикл, что означает, что я могу кодировать не более минуты или около того за один раз, прежде чем переключиться на тестирование (или наоборот). Это означает, что мои тесты будут действительно очень детализированными. Я подумал, что это было одним из преимуществ TDD, но, возможно, я переусердствовал, так что это стало недостатком.
Хенрик Берг
Относительно «достаточно отдельного»: я узнал (опять же из unclebob), что функции должны быть маленькими, и что они должны быть меньше, чем это. Так что в основном я стараюсь сделать 3-4 строковые функции. Таким образом, более или менее вся функциональность разделена на собственные методы, независимо от того, насколько они малы и просты.
Хенрик Берг
В любом случае, я чувствую, что аспекты манипулирования данными (например, findNonNumericFields) действительно должны быть частными. И если я разделю его на другой класс, мне все равно придется обнародовать его, поэтому я не совсем вижу в этом смысл.
Хенрик Берг
6
@HenrikBerg подумайте, почему у вас есть объекты в первую очередь - они не удобные способы группировки функций, а автономные блоки, с которыми проще работать со сложными системами. Следовательно, вы должны думать о тестировании класса как о вещи.
gbjbaanb
@gbjbaanb Я бы сказал, что это одно и то же.
RubberDuck
29

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

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

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

nvoigt
источник
1
«Детали реализации» - это такие вещи, как «использовал ли я XOR или временную переменную, чтобы менять число между переменными» Защищенные / частные методы имеют контракты, как и все остальное. Они берут ввод, работают с ним и производят некоторый результат при определенных ограничениях. Все, что с контрактом, в конечном итоге должно быть проверено - не обязательно для тех, кто использует вашу библиотеку, но для тех, кто поддерживает ее и модифицирует после вас. Просто потому , что это не «общественность» не означает , что она не является частью к API.
Knetic
11

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

Ваши тесты должны быть основаны на том, что класс / функциональность / программа имеет отношение к внешнему миру. В вашем примере будет ли пользователь когда-либо вызывать ваш класс читателя сfind all the non-numerical fields in a line?

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

Поток TDD это:

  • красный (что класс / объект / функция / и т. д. делает с внешним миром)
  • зеленый (напишите минимальный код, чтобы эта функция внешнего мира работала)
  • рефакторинг (какой код лучше для этой работы)

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

Если вы часто пишете тесты для методов, которые становятся частными, вы делаете одну из следующих вещей:

  • Неправильное понимание вашего сценария использования интерфейса / публичного уровня достаточно хорошо, чтобы написать для них тест
  • Значительное изменение вашего дизайна и рефакторинг нескольких тестов (что может быть хорошо, в зависимости от того, тестируется ли эта функциональность в более новых тестах)
enderland
источник
9

Вы сталкиваетесь с распространенным заблуждением при тестировании в целом.

Большинство новичков в тестировании начинают думать так:

  • написать тест для функции F
  • реализовать F
  • написать тест для функции G
  • реализовать G, используя вызов F
  • написать тест для функции H
  • реализовать H, используя вызов G

и так далее.

Проблема здесь в том, что у вас фактически нет модульного теста для функции H. Тест, который должен проверять H, фактически проверяет H, G и F одновременно.

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

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

initcrash
источник
3
Я хотел бы проголосовать еще много раз. Я бы просто подытожил, как, вы не правильно разработали свое решение.
Джастин Омс
В языке, подобном C, это имеет смысл, но для ОО-языков, где единица должна быть классом (с открытыми и закрытыми методами), вам следует тестировать класс, а не каждый закрытый метод в отдельности. Изоляция ваших классов, да. Изоляция методов в каждом классе, нет.
gbjbaanb
8

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

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

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

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

Билл Мичелл
источник
4

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

(Мне не совсем понятно, что делает ваша функция. Возвращает ли она строку с содержимым файла с удалением нечисловых значений?)

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

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

Мэтт Дайер
источник
3

Такое ощущение, что я загнал себя в угол здесь. Но где именно я потерпел неудачу?

Там старая поговорка.

Когда вы не в состоянии планировать, вы планируете потерпеть неудачу.

Люди, кажется, думают, что когда вы используете TDD, вы просто садитесь, пишете тесты, и дизайн просто волшебным образом происходит. Это не правда Вам нужно иметь план высокого уровня. Я обнаружил, что я получаю наилучшие результаты от TDD, когда я сначала проектирую интерфейс (публичный API). Лично я создаю фактическое, interfaceкоторое определяет класс первым.

задыхаясь, я написал какой-то "код", прежде чем писать какие-либо тесты! Ну нет. Я не Я написал контракт, которому нужно следовать, дизайн . Я подозреваю, что вы могли бы получить аналогичные результаты, набросав UML-диаграмму на миллиметровой бумаге. Дело в том, что у вас должен быть план. TDD не является лицензией на то, чтобы взломать кусок кода.

Я действительно чувствую, что «Test First» - это неправильное название. Дизайн Во- первых , то испытание.

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

Резиновая утка
источник
2

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

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

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

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

Warbo
источник
1
Хотя другие ответы верны в том смысле, что частный метод не должен был быть написан в красном цикле, люди совершают ошибки. И когда вы достаточно далеко пошли по пути ошибки, это подходящее решение.
Slebetman
2

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

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

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

Томас Эндрюс
источник
2
«Бывают случаи, когда закрытый метод можно сделать общедоступным методом другого класса». Я не могу подчеркнуть это достаточно. Иногда закрытый метод - это просто другой класс, кричащий о своей идентичности.
0

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

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

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

9000
источник
3
Изменение модификатора доступа к методу только для запуска тестов - это ситуация, когда хвост шевелит собаку. Я думаю, что тестирование внутренних частей устройства только усложняет последующий рефакторинг. Тестирование общедоступного интерфейса, наоборот, прекрасно, потому что он работает как «контракт» для модуля.
скрипт
Вам не нужно менять уровень доступа метода. Я пытаюсь сказать, что у вас есть промежуточный уровень доступа, который позволяет легче писать определенный код, включая тесты, без заключения публичного контракта. Конечно, вы должны протестировать открытый интерфейс, но кроме того, иногда полезно тестировать некоторые внутренние операции изолированно.
9000
0

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

Брайан Кноблаух
источник
0

Я испытал это и почувствовал вашу боль.

Мое решение было:

прекратить относиться к испытаниям, как к строительству монолита.

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

Например, я часто имею:

  • тест низкого уровня 1
  • код для удовлетворения
  • тест низкого уровня 2
  • код для удовлетворения
  • тест низкого уровня 3
  • код для удовлетворения
  • тест низкого уровня 4
  • код для удовлетворения
  • тест низкого уровня 5
  • код для удовлетворения

так что у меня есть

  • тест низкого уровня 1
  • тест низкого уровня 2
  • тест низкого уровня 3
  • тест низкого уровня 4
  • тест низкого уровня 5

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

  • тест низкого уровня 1
  • тест низкого уровня 5

Дьявол кроется в деталях, и способность делать это будет зависеть от обстоятельств.

Майкл Даррант
источник
-2

Солнце вращается вокруг земли или земля вокруг солнца? По мнению Эйнштейна, ответ «да» или обе, поскольку обе модели отличаются только с точки зрения, аналогично, инкапсуляция и разработка, основанная на тестировании, конфликтуют только потому, что мы думаем, что это так. Мы сидим здесь, как Галилей и папа, обижаясь друг на друга: дурак, разве ты не видишь, что частные методы тоже нуждаются в проверке; еретик, не нарушай инкапсуляцию! Аналогично, когда мы признаем, что истина грандиознее, чем мы думали, мы можем попробовать что-то вроде инкапсуляции тестов для частных интерфейсов, чтобы тесты для открытых интерфейсов не нарушали инкапсуляцию.

Попробуйте это: добавьте два метода, один из которых не имеет входных данных, но justs возвращает количество закрытых тестов, а другой принимает число тестов в качестве параметра и возвращает pass / fail.

hildred
источник
1
Выбранные оскорбления были использованы Галилеем и Папой, а не каким-либо ответом на этот вопрос.
hildred