Краткое введение в этот вопрос. Я использовал сейчас TDD и в последнее время BDD уже более года. Я использую такие приемы, как издевательство, чтобы писать свои тесты более эффективно. В последнее время я начал личный проект, чтобы написать небольшую программу управления капиталом для себя. Поскольку у меня не было устаревшего кода, это был идеальный проект, чтобы начать с TDD. К сожалению, я не испытал столько радости от TDD. Это даже настолько испортило мое веселье, что я разочаровался в проекте.
В чем была проблема? Ну, я использовал подход, подобный TDD, чтобы тесты / требования развивали дизайн программы. Проблема заключалась в том, что более половины времени разработки приходилось на написание / рефакторинг тестов. В итоге я не хотел реализовывать больше функций, потому что мне нужно было бы провести рефакторинг и написать множество тестов.
На работе у меня много устаревшего кода. Здесь я пишу все больше интеграционных и приемочных тестов и меньше юнит-тестов. Это не кажется плохим подходом, поскольку ошибки в основном выявляются при приемочных и интеграционных тестах.
Моя идея состояла в том, чтобы я мог в итоге написать больше интеграционных и приемочных тестов, чем модульных. Как я уже говорил, для выявления ошибок юнит-тесты не лучше, чем интеграционные / приемочные тесты. Модульные испытания также хороши для дизайна. Так как я писал много из них, мои занятия всегда были хорошими для тестирования. Кроме того, подход к тестированию / требованиям, которым следует руководствоваться при проектировании, в большинстве случаев приводит к улучшению дизайна. Последнее преимущество юнит-тестов в том, что они быстрее. Я написал достаточно интеграционных тестов, чтобы знать, что они могут быть почти такими же быстрыми, как и модульные тесты.
После того, как я искал через Интернет , я обнаружил, что есть очень похожие идеи , помоему , упомянутые здесь , и там . Что вы думаете об этой идее?
редактировать
Отвечая на вопросы, один пример, где дизайн был хорош, но мне потребовался огромный рефакторинг для следующего требования:
Сначала были некоторые требования для выполнения определенных команд. Я написал расширяемый анализатор команд, который анализировал команды из какой-либо командной строки и вызывал правильную в модели. Результат был представлен в классе модели представления:
Здесь не было ничего плохого. Все классы были независимы друг от друга, и я мог легко добавлять новые команды, показывать новые данные.
Следующим требованием было, чтобы у каждой команды было собственное представление представления - своего рода предварительный просмотр результата команды. Я переработал программу, чтобы добиться лучшего дизайна для нового требования:
Это было также хорошо, потому что теперь у каждой команды есть своя собственная модель представления и, следовательно, собственный предварительный просмотр.
Дело в том, что синтаксический анализатор команд был изменен на использование анализа команд на основе токенов и лишен возможности выполнять команды. Каждая команда получила свою собственную модель представления, и модель представления данных знает только текущую модель представления команды, которая знает данные, которые должны быть показаны.
Все, что я хотел бы знать на данный момент, - если новый дизайн не нарушает каких-либо существующих требований. Мне не нужно было менять ЛЮБОЙ мой приемочный тест. Мне пришлось провести рефакторинг или удалить почти КАЖДЫЕ юнит-тесты, что было огромной кучей работы.
Здесь я хотел показать общую ситуацию, которая часто случалась во время разработки. Не было никаких проблем со старым или новым дизайном, они просто менялись естественным образом в соответствии с требованиями - насколько я понял, это одно из преимуществ TDD в том, что дизайн развивается.
Заключение
Спасибо за все ответы и обсуждения. Подводя итог этой дискуссии, я подумал о подходе, который я опробую в своем следующем проекте.
- Прежде всего, я пишу все тесты, прежде чем реализовать что-либо, как всегда делал.
- Для требований я пишу сначала несколько приемочных тестов, которые тестируют всю программу. Затем я пишу несколько интеграционных тестов для компонентов, где мне нужно реализовать требование. Если есть компонент, который тесно взаимодействует с другим компонентом для реализации этого требования, я бы также написал несколько интеграционных тестов, в которых оба компонента тестируются вместе. И последнее, но не менее важное, если мне нужно написать алгоритм или любой другой класс с высокой перестановкой - например, сериализатор - я бы написал модульные тесты для этих конкретных классов. Все остальные классы не тестируются, а проходят какие-либо юнит-тесты.
- Для ошибок этот процесс может быть упрощен. Обычно ошибка вызвана одним или двумя компонентами. В этом случае я бы написал один интеграционный тест для компонентов, который тестирует ошибку. Если бы это касалось алгоритма, я бы написал только тестовый модуль. Если не легко обнаружить компонент, где возникает ошибка, я бы написал приемочный тест, чтобы найти ошибку - это должно быть исключением.
источник
Ответы:
Это сравнивает апельсины и яблоки.
Интеграционные тесты, приемочные тесты, модульные тесты, поведенческие тесты - все это тесты, и все они помогут вам улучшить ваш код, но они также совершенно разные.
Я собираюсь пройтись по каждому из разных тестов и, надеюсь, объяснить, почему вам нужно сочетание всех из них:
Интеграционные тесты:
Просто проверьте, правильно ли интегрируются различные компоненты вашей системы, например, возможно, вы смоделировали запрос веб-службы и убедитесь, что результат возвращается. Я бы обычно использовал реальные (ish) статические данные и фиктивные зависимости, чтобы гарантировать их постоянную проверку.
Приемочные испытания:
Приемочный тест должен напрямую соответствовать бизнес-сценарию. Он может быть огромным («сделки представлены правильно») или крошечным («фильтр успешно фильтрует список») - это не имеет значения; важно то, что он должен быть явно привязан к конкретному требованию пользователя. Я хотел бы сосредоточиться на них для разработки на основе тестов, потому что это означает, что у нас есть хорошее справочное руководство по тестам для пользовательских историй для dev и qa для проверки.
Модульные тесты:
Для небольших отдельных функциональных блоков, которые могут составлять или не составлять отдельную пользовательскую историю - например, пользовательская история, которая говорит, что мы получаем всех клиентов, когда мы получаем доступ к определенной веб-странице, может быть приемочным тестом (имитировать попадание в Интернет). страницы и проверки ответа), но также может содержать несколько модульных тестов (проверить, проверены ли разрешения безопасности, убедиться, что соединение с базой данных запрашивает правильно, убедиться, что любой код, ограничивающий количество результатов, выполнен правильно) - все это «модульные тесты» это не полный приемочный тест.
Поведенческие тесты:
Определите, каким должен быть поток приложения в случае конкретного ввода. Например, «когда соединение не может быть установлено, убедитесь, что система повторяет соединение». Опять же, вряд ли это будет полный приемочный тест, но он все же позволит вам проверить что-то полезное.
Это все на мой взгляд благодаря большому опыту написания тестов; Я не люблю сосредотачиваться на подходах к учебникам, а скорее на том, что дает ценность вашим тестам.
источник
TL; DR: Пока это отвечает вашим потребностям, да.
Я занимаюсь разработкой Acceptance Test Driven Development (ATDD) уже много лет. Это может быть очень успешным. Есть несколько вещей, о которых нужно знать.
Теперь преимущества
Как всегда, вы должны сделать анализ и выяснить, подходит ли эта практика для вашей ситуации. В отличие от многих людей, я не думаю, что есть идеализированный правильный ответ. Это будет зависеть от ваших потребностей и требований.
источник
Модульные тесты работают лучше всего, когда открытый интерфейс компонентов, для которых они используются, меняется не слишком часто. Это означает, что когда компоненты уже спроектированы правильно (например, следуя принципам SOLID).
Поэтому полагать, что хороший дизайн просто «развивается» из «бросания» множества модульных тестов на компонент, является ошибкой. TDD не является «учителем» для хорошего дизайна, он может лишь немного помочь убедиться, что определенные аспекты дизайна хороши (особенно тестируемость).
Когда ваши требования изменятся, и вам придется изменить внутренние компоненты компонента, и это нарушит 90% ваших модульных тестов, поэтому вам придется очень часто их реорганизовывать, тогда, скорее всего, дизайн был не таким хорошим.
Поэтому мой совет: подумайте о дизайне компонентов, которые вы создали, и о том, как вы можете сделать их более подходящими по принципу открытого / закрытого. Идея последнего состоит в том, чтобы гарантировать, что функциональность ваших компонентов может быть расширена позже, не меняя их (и, таким образом, не нарушая API компонента, используемого вашими модульными тестами). Такие компоненты могут (и должны быть) покрыты тестами модульных тестов, и опыт не должен быть таким болезненным, как вы его описали.
Когда вы не можете придумать такой дизайн сразу, лучше начать приемочные и интеграционные тесты.
РЕДАКТИРОВАТЬ: Иногда дизайн ваших компонентов может быть в порядке, но дизайн ваших модульных тестов может вызвать проблемы . Простой пример: вы хотите проверить метод «MyMethod» класса X и написать
(предположим, что значения имеют какое-то значение).
Предположим далее, что в рабочем коде есть только один вызов
X.MyMethod
. Теперь, для нового требования, метод «MyMethod» нуждается в дополнительном параметре (например, что-то вродеcontext
), который нельзя пропустить. Без модульных тестов нужно было бы реорганизовать вызывающий код в одном месте. С юнит-тестами нужно провести рефакторинг 500 мест.Но причина здесь не в самих модульных тестах, а в том, что один и тот же вызов X.MyMethod повторяется снова и снова, не строго следуя принципу «Не повторяйся (СУХОЙ)». здесь нужно поместить тестовые данные и связанные с ними ожидаемые значения в список и выполнить вызовы «MyMethod» в цикле (или, если инструмент тестирования поддерживает так называемые «тесты накопителя данных», использовать эту функцию). количество мест, которые нужно изменить в модульных тестах, когда сигнатура метода меняется на 1 (вместо 500).
В вашем реальном случае ситуация может быть более сложной, но я надеюсь, что вы поняли - когда ваши модульные тесты используют API компонентов, для которого вы не знаете, может ли он стать объектом изменений, убедитесь, что вы уменьшаете число вызовов этого API к минимуму.
источник
X x= new X(); AssertTrue(x.MyMethod(12,"abc"))
перед тем, как реализовать метод. Используя предварительный дизайн, вы можетеclass X{ public bool MyMethod(int p, string q){/*...*/}}
сначала написать , а потом написать тесты. В обоих случаях вы приняли одно и то же дизайнерское решение. Если решение было хорошим или плохим, TDD не сообщит вам.Да, конечно.
Учти это:
Смотрите общую разницу ....
Проблема заключается в покрытии кода, если вы можете выполнить полное тестирование всего своего кода с помощью интеграционного / приемочного тестирования, то это не проблема. Ваш код проверен. Это цель.
Я думаю, что вам, возможно, придется смешать их, так как каждый проект на основе TDD потребует некоторого интеграционного тестирования, чтобы убедиться, что все модули действительно хорошо работают вместе (я знаю из опыта, что 100% пройденная модульная кодовая база не обязательно работает когда ты их всех собрал!)
Проблема действительно сводится к простоте тестирования, отладки сбоев и их устранения. Некоторые люди считают, что их модульные тесты очень хороши в этом, они маленькие и простые, а ошибки легко увидеть, но недостатком является то, что вы должны реорганизовать свой код в соответствии с инструментами модульного тестирования и написать очень много из них. Интеграционный тест труднее написать, чтобы покрыть большую часть кода, и вам, вероятно, придется использовать такие методы, как ведение журнала, для отладки любых сбоев (хотя, я бы сказал, что вы должны делать это в любом случае, вы не можете откатить тесты модулей) когда на месте!).
В любом случае, вы все еще получаете проверенный код, вам просто нужно решить, какой механизм вам больше подходит. (Я бы пошел с небольшим количеством микса, проверил комплексные алгоритмы и интегрировал бы тестирование остальных).
источник
Я думаю, что это ужасная идея.
Поскольку приемочные тесты и тестирование интеграции затрагивают более широкие части вашего кода для тестирования конкретной цели, со временем им потребуется больше рефакторинга, а не меньше. Хуже того, поскольку они охватывают широкие разделы кода, они увеличивают время, которое вы тратите на поиск первопричины, поскольку у вас есть более широкая область для поиска.
Нет, вы обычно должны писать больше юнит-тестов, если у вас нет нечетного приложения с 90% пользовательским интерфейсом или чего-то еще, что неудобно для юнит-теста. Боль, с которой вы сталкиваетесь, связана не с юнит-тестами, а с разработкой тестов. Как правило, вы должны тратить только 1/3 своего времени на большинство написания тестов. В конце концов, они здесь, чтобы служить вам, а не наоборот.
источник
«Победа» с TDD заключается в том, что после написания тестов их можно автоматизировать. С другой стороны, это может занять значительную часть времени разработки. Является ли это на самом деле замедляет весь процесс вниз спорно. Аргумент заключается в том, что предварительное тестирование уменьшает количество ошибок, которые необходимо исправить в конце цикла разработки.
Именно здесь BDD входит, так как поведение может быть включено в модульное тестирование, поэтому процесс по определению является менее абстрактным и более осязаемым.
Ясно, что если бы было доступно бесконечное количество времени, вы бы провели как можно больше испытаний различных сортов. Однако время, как правило, ограничено, и непрерывное тестирование является экономически эффективным только до определенной степени.
Все это приводит к выводу, что тесты, которые обеспечивают наибольшую ценность, должны быть в начале процесса. Само по себе это не означает автоматическое предпочтение одного типа тестирования другому - более того, каждый случай должен быть взят по существу.
Если вы пишете виджет командной строки для личного использования, вас в первую очередь заинтересуют юнит-тесты. В то время как веб-служба, скажем, потребует значительного объема интеграционного / поведенческого тестирования.
В то время как большинство типов тестов концентрируются на том, что можно назвать «гоночной линией», т. Е. Тестируют то, что требуется современному бизнесу, модульное тестирование отлично подходит для устранения мелких ошибок, которые могут появиться на более поздних этапах разработки. Поскольку это преимущество не поддается измерению, его часто упускают из виду.
источник
Это ключевой момент, а не только «последнее преимущество». Когда проект становится все больше и больше, ваши приемочные тесты интеграции становятся все медленнее и медленнее. И здесь, я имею в виду настолько медленно, что вы перестанете их выполнять.
Конечно, юнит-тесты также становятся медленнее, но они все же более чем на порядок быстрее. Например, в моем предыдущем проекте (c ++, около 600 kLOC, 4000 модульных тестов и 200 интеграционных тестов) потребовалось около одной минуты, чтобы выполнить все, и более 15, чтобы выполнить интеграционные тесты. Для создания и выполнения модульных тестов для изменяемой детали в среднем потребуется менее 30 секунд. Когда вы можете сделать это так быстро, вы захотите делать это все время.
Просто чтобы прояснить: я не говорю не добавлять интеграционные и приемочные тесты, но похоже, что вы сделали TDD / BDD неправильно.
Да, проектирование с учетом тестируемости сделает проект лучше.
Что ж, когда требования меняются, вы должны изменить код. Я бы сказал, что вы не закончили свою работу, если не написали модульные тесты. Но это не значит, что вы должны иметь 100% охват модульными тестами - это не цель. Некоторые вещи (например, GUI или доступ к файлу, ...) даже не предназначены для модульного тестирования.
Результатом этого является лучшее качество кода и еще один уровень тестирования. Я бы сказал, что оно того стоит.
У нас также было несколько приёмочных тестов, и на их выполнение уйдет целая неделя.
источник