Некоторое время я пытался научиться писать модульные тесты для своего кода.
Первоначально я начал делать настоящий TDD, где я не буду писать никакого кода, пока сначала не напишу провальный тест.
Тем не менее, недавно у меня возникла острая проблема, которая требует большого количества кода. Потратив пару недель на написание тестов, а затем кода, я пришел к печальному выводу, что весь мой подход не сработает, и мне придется бросить две недели работы и начинать заново.
Это достаточно плохое решение, когда вы только что написали код, но когда вы также написали несколько сотен модульных тестов, становится еще более эмоционально трудно просто выбросить все это.
Я не могу не думать, что я потратил 3 или 4 дня усилий на написание этих тестов, когда я мог просто собрать код для проверки концепции, а затем написать тесты после того, как я был доволен своим подходом.
Как люди, которые практикуют TDD, правильно справляются с такими ситуациями? Есть ли основания для нарушения правил в некоторых случаях, или вы всегда сначала пишете тесты по-рабски, даже если этот код может оказаться бесполезным?
источник
Ответы:
Я чувствую, что здесь есть две проблемы. Во-первых, вы заранее не поняли, что ваш оригинальный дизайн может быть не лучшим подходом. Если бы вы знали об этом заранее, возможно, вы решили разработать один - два быстрых прототипа , чтобы изучить возможные варианты дизайна и оценить, какой из них является наиболее перспективным. При создании прототипов вам не нужно писать производственный код качества и не нужно проводить модульное тестирование каждого закоулка (или вообще), поскольку ваш единственный акцент делается на обучении, а не на полировке кода.
Теперь понять, что вам нужно создавать прототипы и проводить эксперименты, а не начинать разработку производственного кода сразу, не всегда легко и даже не всегда возможно. Вооружившись только что полученными знаниями, вы сможете распознать необходимость создания прототипа в следующий раз. Или не может. Но, по крайней мере, теперь вы знаете, что этот вариант должен быть рассмотрен. И это само по себе является важным знанием.
Другая проблема - ИМХО с вашим восприятием. Мы все совершаем ошибки, и в ретроспективе так легко увидеть, что мы должны были сделать по-другому. Это просто способ, которым мы учимся. Запишите свои инвестиции в модульные тесты, как цену изучения того, что прототипирование может быть важным, и преодолейте это. Просто старайся не повторять одну и ту же ошибку дважды :-)
источник
Смысл TDD в том, что он заставляет вас писать небольшие приращения кода в маленьких функциях , чтобы избежать этой проблемы. Если вы потратили недели на написание кода для одного домена, и каждый написанный вами служебный метод становится бесполезным, когда вы переосмысливаете архитектуру, тогда ваши методы почти наверняка слишком велики. (Да, я знаю, что сейчас это не совсем утешительно ...)
источник
Брукс сказал: «Планируй выбросить один, все равно будешь». Мне кажется, что вы делаете именно это. Тем не менее, вы должны написать свои модульные тесты, чтобы проверить блок кода, а не большой объем кода. Это более функциональные тесты, и, следовательно, они должны проходить через любую внутреннюю реализацию.
Например, если я хочу написать решатель уравнений в частных производных (PDE), я бы написал несколько тестов, пытаясь решить задачи, которые я могу решить математически. Это мои первые «модульные» тесты - читай: функциональные тесты выполняются как часть фреймворка xUnit. Они не изменятся в зависимости от того, какой алгоритм я использую для решения PDE. Все, что меня волнует, это результат. Второе модульное тестирование будет сосредоточено на функциях, используемых для кодирования алгоритма, и, следовательно, будет зависеть от алгоритма - скажем, Рунге-Кутта. Если бы я узнал, что Рунге-Кутта не подходит, то у меня все равно были бы те тесты высшего уровня (включая те, которые показали, что Рунге-Кутта не подходит). Таким образом, вторая итерация будет по-прежнему иметь те же тесты, что и первая.
Ваша проблема может быть в дизайне, а не в коде. Но без подробностей сложно сказать.
источник
Следует помнить, что TDD - это итеративный процесс. Напишите небольшой тест (в большинстве случаев достаточно нескольких строк) и запустите его. Тест должен провалиться, теперь он работает непосредственно с вашим основным источником и попытается реализовать протестированную функциональность, чтобы тест прошел. Теперь начните все сначала.
Вы не должны пытаться писать все тесты за один раз, потому что, как вы заметили, это не сработает. Это снижает риск тратить ваше время на написание тестов, которые не будут использоваться.
источник
Я думаю, что вы сказали это сами: вы не были уверены в своем подходе до того, как начали писать все свои модульные тесты.
То, чему я научился, сравнивая реальные проекты TDD, с которыми я работал (не так много на самом деле, всего 3, охватывающих 2 года работы) с тем, что я усвоил теоретически, это то, что Automated Testing! = Unit Testing (конечно, не будучи взаимно эксклюзивные).
Другими словами, T в TDD не обязательно должен иметь с собой U ... Он автоматизирован, но является менее модульным тестом (как в классах и методах тестирования), чем автоматизированным функциональным тестом: он находится на том же уровне функциональной детализации как архитектуры, над которой вы сейчас работаете. Вы начинаете с высокого уровня, с несколькими тестами и только функциональной общей картиной, и только в конечном итоге вы получаете тысячи UT, и все ваши классы четко определены в красивой архитектуре ...
Модульные тесты дают вам большую помощь, когда вы работаете в команде, чтобы избежать изменений кода, создающих бесконечные циклы ошибок. Но я никогда не писал ничего такого точного, когда начинал работать над проектом, прежде чем иметь хотя бы глобальный рабочий POC для каждой пользовательской истории.
Может быть, это только мой личный способ сделать это. У меня нет достаточного опыта, чтобы с нуля решить, какие шаблоны или структуру будет иметь мой проект, так что, действительно, я не буду тратить свое время на написание сотен UT с самого начала ...
В более общем смысле, идея разбить все и бросить все это всегда будет. Как бы мы ни старались быть «непрерывными» с нашими инструментами и методами, иногда единственный способ бороться с энтропией - начать все сначала. Но цель в том, что когда это произойдет, автоматизированное и модульное тестирование, которое вы внедрили, сделает ваш проект уже менее затратным, чем если бы его там не было - и будет, если вы найдете равновесие.
источник
Сопоставление модульного тестирования с разработкой, основанной на тестировании, является источником многих страданий и горя. Итак, давайте рассмотрим это еще раз:
В итоге: модульное тестирование ориентировано на реализацию, TDD - на требования. Они не одно и то же.
источник
Разработка, основанная на тестировании, предназначена для управления вашей разработкой. Тесты, которые вы пишете, помогают утверждать правильность кода, который вы сейчас пишете, и увеличивают скорость разработки с первой строки.
Похоже, вы верите, что тесты - это бремя и предназначены только для последующего развития. Такое мышление не соответствует TDD.
Возможно, вы можете сравнить его со статической типизацией: хотя можно писать код, не используя информацию о статических типах, добавление статического типа в код помогает утверждать определенные свойства кода, освобождая разум и позволяя вместо этого сосредоточиться на важной структуре, увеличивая тем самым скорость и скорость. эффективность.
источник
Проблема с проведением крупного рефакторинга заключается в том, что вы можете и иногда будете следовать по пути, который заставит вас осознать, что вы откусили больше, чем можете прожевать. Гигантские рефакторинги - это ошибка. Если конструкция системы изначально несовершенна, то рефакторинг может отвести вас далеко вперед, прежде чем вам нужно будет принять трудное решение. Либо оставьте систему такой, какая она есть, и обойдите ее, либо запланируйте редизайн и внесите некоторые существенные изменения.
Однако есть и другой способ. Реальное преимущество рефакторинга кода состоит в том, чтобы сделать вещи проще, легче читать и еще легче поддерживать. Когда вы подходите к проблеме, в которой вы не уверены, вы вносите изменения, заходите так далеко, чтобы посмотреть, к чему это может привести, чтобы узнать больше о проблеме, затем выбрасываете пик и применяете новый рефакторинг, основанный на том, что такое пик. научил тебя. Дело в том, что вы действительно можете с уверенностью улучшить свой код, только если шаги невелики и ваши усилия по рефакторингу не перевешивают вашу способность сначала писать тесты. Соблазн состоит в том, чтобы написать тест, затем код, затем кодировать еще немного, потому что решение может показаться очевидным, но вскоре вы понимаете, что ваше изменение изменит намного больше тестов, поэтому вам нужно быть осторожным, чтобы менять только одну вещь за раз.
Поэтому ответ никогда не должен превращать ваш рефакторинг в основной. Шаги малыша. Начните с извлечения методов, затем посмотрите на удаление дублирования. Затем перейдите к извлечению классов. Каждый в крошечных шагах одно незначительное изменение за один раз. Если вы извлекаете код, сначала напишите тест. Если вы удаляете код, удалите его, запустите тесты и решите, понадобятся ли какие-либо из сломанных тестов. Один крошечный шаг за шагом. Кажется, что это займет больше времени, но на самом деле значительно сократит время рефакторинга.
Однако реальность такова, что каждый спайк является, по-видимому, потенциальной тратой усилий. Изменения кода иногда никуда не ведут, и вы обнаруживаете, что восстанавливаете свой код из ваших VCS. Это просто реальность того, что мы делаем изо дня в день. Однако каждый спайк, который терпит неудачу, не теряется, если он чему-то вас учит. Каждое неудачное рефакторинг научит вас, что вы либо пытаетесь сделать слишком много слишком быстро, либо что ваш подход может быть неправильным. Это тоже не пустая трата времени, если вы чему-то научитесь. Чем больше вы делаете это, тем больше вы учитесь и тем эффективнее вы становитесь. Мой совет - просто наденьте его сейчас, научитесь делать больше, делая меньше, и признайте, что это, вероятно, так и должно быть, пока вы не станете лучше определять, как далеко завести шип, прежде чем он приведет вас в никуда.
источник
Я не уверен в причине, почему ваш подход оказался ошибочным через 3 дня. В зависимости от вашей неопределенности в вашей архитектуре, вы можете рассмотреть возможность изменения стратегии тестирования:
Если вы не уверены в производительности, возможно, вы захотите начать с нескольких интеграционных тестов, которые подтверждают производительность?
Когда вы исследуете сложность API, напишите несколько простых, небольших модульных тестов, чтобы выяснить, как лучше всего это сделать. Не беспокойтесь о реализации чего-либо, просто заставьте ваши классы возвращать жестко закодированные значения или заставьте их генерировать NotImplementedExceptions.
источник
Для меня модульные тесты также являются поводом для «реального» использования интерфейса (ну, так же реально, как идут юнит-тесты!).
Если я вынужден провести тест, я должен использовать свой дизайн. Это помогает держать вещи в здравом уме (если что-то настолько сложное, что написание теста для него является обузой, каково это будет использовать его?).
Это не избегает изменений в дизайне, скорее это выставляет потребность в них. Да, полное переписывание - это боль. Чтобы (попытаться) избежать этого, я обычно создаю (один или несколько) прототипов, возможно на Python (с окончательной разработкой на c ++).
Конечно, у вас не всегда есть время для всех этих вкусностей. Это как раз те случаи, когда вам потребуется БОЛЬШЕ времени для достижения ваших целей ... и / или чтобы держать все под контролем.
источник
Добро пожаловать в цирк креативных разработчиков .
Вместо того, чтобы уважать все «законные / разумные» способы кодирования в начале,
попробуйте интуицию , прежде всего, если это важно и ново для вас, и если ни один пример не выглядит так, как вы хотите:
- Пишите со своим инстинктом из вещей, которые вы уже знаете не твоим умом и воображением.
- И остановись.
- Возьмите лупу и осмотрите все слова, которые вы пишете: вы пишете «текст», потому что «текст» близок к строке, но требуется «глагол», «прилагательное» или что-то более точное, прочитайте еще раз и настройте метод с новым смыслом
. .. или вы написали кусок кода, думая о будущем? удалите это
- исправьте, сделайте другое задание (спорт, культура или другие вещи вне бизнеса), вернитесь и прочитайте снова.
- Все хорошо вписывается,
- Поправь, сделай другое задание, вернись и прочитай снова.
- Все хорошо вписывается, переходите к TDD
- Теперь все правильно, хорошо
- Попробуйте эталонный тест, чтобы указать на вещи, которые нужно оптимизировать, сделайте это.
Что появляется:
- вы написали кодекс, соблюдая все правила
- вы получаете опыт, новый способ работы,
- что-то изменится в вашем сознании, вы никогда не будете бояться новой конфигурации.
И теперь, если вы увидите UML, похожий на приведенный выше, вы сможете сказать:
«Босс, я начну с TDD для этого…».
Это еще одна новая вещь?
«Босс, я бы попробовал кое-что, прежде чем определиться с тем, как я буду кодировать…» С
наилучшими пожеланиями от PARIS
Claude
источник