TDD, новые тесты, а старые еще не реализованы

13

Я экспериментирую с разработкой на основе тестов и обнаружил, что часто сталкиваюсь со следующей ситуацией:

  1. Я пишу тесты для некоторой функциональности X. Эти тесты не проходят.
  2. Пытаясь реализовать X, я вижу, что мне нужно реализовать некоторую функцию Y на нижнем уровне моего кода. Так...
  3. Я пишу тесты для Y. Теперь оба теста для X и Y не пройдены.

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

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

Что мне делать в таких случаях? Есть ли у TDD какие-либо рекомендации?

liori
источник

Ответы:

9

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

Майкл Браун
источник
Мои тесты обычно не знают, что должен делать метод внутри (например, какой API-интерфейс более низкого уровня вызывать). Должен ли я просто настроить тесты, чтобы имитировать все, что мне нужно в проверенном коде?
Лиори
2
Точно так же ваши протестированные классы не должны заботиться о том, что делают «нижние уровни». Используйте макеты / заглушки вместо реальных классов / объектов. Это может потребовать немного больше усилий при разработке, но приводит к тому, что код будет менее связанным и более простым для повторного использования.
Mchl
1
Вы используете инъекцию зависимости? Таким образом вы можете легко отделить проблемы более низкого уровня от классов более высокого уровня. У вашего тестируемого класса есть конструктор с параметрами для его зависимостей (в качестве интерфейсов). В вашем тесте вы создаете макеты для интерфейсов. По сути, вы притворяетесь, что уже внедрили сервисы более низкого уровня.
Майкл Браун
@ Майк Браун, да. Я знаю, что могу создавать фиктивные объекты. Но затем в моем тесте на функциональность Xя должен знать, какую часть зависимостей Xмне нужно смоделировать. Я чувствую, что это часть деталей реализации, которая не должна быть частью тестов, иначе мне может понадобиться изменить тесты при рефакторинге реализации. Должен ли я беспокоиться об этом?
Лиори
1
Вовсе нет ... тесты должны отражать предположения тестируемой системы. Это также поможет вам получить то, что вам нужно от служб, на которые опирается система. Раньше я соглашался с вами по этому вопросу, но я сравниваю его с тем, как я пришел к пониманию рекурсивного программирования. Сначала вы пишете код, предполагая, что у вас есть функция, которая делает то, что вы хотите. Затем вы пишете код, который делает то, что вы хотите.
Майкл Браун
4

Заглушки и макеты могут быть использованы для имитации функциональности, которая еще не была изменена / реализована. Они также могут помочь вам разрешить зависимости, которые вызывают подобную «цепную реакцию».

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

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

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

ratkok
источник
Ха, я искал возможность отключить тест во время тестового прогона в моей IDE и не нашел его. Теперь я обнаружил, что в Python unittestуже есть тестовые пропуски. Это может быть достаточно для меня.
Лиори
Мы используем google test C ++ framework - и у него есть возможность отключить тесты. Отключенные тесты не выполняются, но компилируются - в тот момент, когда они вам нужны - они готовы к запуску (кроме того, вы можете «принудительно выполнить» отключенные тесты - своего рода «включение во время выполнения») - отличная функция ...
ratkok
3

Стоп

Неожиданно, похоже, что здесь могут быть две отдельные проблемы:

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

  2. вы на самом деле делать модульное тестирование, а не TDD функции тестирования

Для # 1 остановитесь , вернитесь назад и обновите истории и тестовые сценарии, затем начните сначала с другого сценария.

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

Стивен А. Лоу
источник
Мне очень нравится ваш ответ, он лучше объясняет, что на самом деле происходит.
maple_shaft
... С учетом сказанного я не знаю в мире премьер-министра, который не будет полностью сходить с ума при фразе "СТОП, нам нужно отступить". Они попробуют все, кроме того, что пожертвуют своим первенцем на алтаре, чтобы продвинуть проект вперед, проклять технический долг и неполные юнит-тесты. Я думаю, вы не можете винить их, когда их единственная метрика в организации - это своевременное выполнение проекта. Некоторые организации просто ценят время, а не качество, и именно поэтому я, вероятно, никогда не видел, чтобы TDD успешно работал в организациях такого типа, что, к сожалению, МОЖЕТ из них.
maple_shaft
@maple_shaft: количество времени, которое вы останавливаете, чтобы перегруппироваться, может составлять всего несколько часов - если ваш процесс слишком далек от базы, в этом случае остановка на несколько дней, чтобы вернуть его в нужное русло, значительно повысит вероятность того, что Проект будет успешным. Нет смысла идти полным ходом вниз по неверному пути!
Стивен А. Лоу,
0

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

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

Опытный образец

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

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

Вы должны знать больше об API нижнего уровня на этом этапе и быть в состоянии успешно смоделировать API нижнего уровня в ваших компонентах более высокого уровня.

maple_shaft
источник
Таким образом, вы на самом деле предлагаете получить больше информации для фазы планирования, выполняя некоторое предварительное кодирование неформальным (не используя формальную методологию) способом. А затем предположим, что он даст достаточно информации для планирования реального кода. Я прав?
Лиори
Почему вы предполагаете, что прототипирование - это неформальный процесс? Каждая оценка должна учитывать прототипирование, а графики проекта должны учитывать его, а также необходимую задачу разработки. Я рассматриваю это так же, как Дизайн или Code-Review. На этом примечании это формализовано и должно быть учтено, даже больше на задачах с большим количеством неизвестных. Без создания прототипов и возможности проверки на прочность концепции, реализация TDD просто предполагает, что разработчики знают ВСЕ о НИЧЕМ со ВСЕМИ функциями. Реальный мир не работает таким образом, и мне все равно, насколько вы умны или опытны.
maple_shaft
Под «неформальным способом» я не имел в виду, что время для прототипирования не должно учитываться, но когда вы создаете прототипы, вы не следуете TDD или любой другой методологии кода.
Лиори
TDD - это методология модульного тестирования и разработки. Имеет ли смысл использовать TDD для проверки кода? Имеет ли смысл TDD для дизайна, написания технических спецификаций или интерактивной доски? Прототипирование - это задача сама по себе, исследовательский тип разработки для исследования, доказательства концепции и образования.
maple_shaft
1
TDD делает идеальный смысл для прототипирования. Это позволяет вам быстро представить все, что у вас есть (объект, функция, API, вся программа) в виде повторяемого исполняемого набора требований. Сделайте себе одолжение и прочитайте Растущее объектно-ориентированное программное обеспечение под руководством тестов ; он шаг за шагом проходит через создание всего приложения (включая интеграцию) в тестовом режиме.
Фрэнк Ширар
0

Это зависит от того, какие тесты вы пишете во время выполнения TDD.

Классическая модель заключается в написании модульных тестов и использовании макетов или заглушек для отделения теста от других «блоков» кода.

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

dietbuddha
источник