У нас стартовал новый (довольно большой) проект, который мы планировали развивать с использованием TDD.
Идея TDD провалилась (по многим деловым и некоммерческим причинам), но сейчас у нас есть разговор - должны ли мы в любом случае писать модульные тесты или нет. Мой друг говорит, что нет смысла (или близок к нулю) писать модульные тесты без TDD, мы должны сосредоточиться только на интеграционных тестах. Я верю в обратное, что в написании простых модульных тестов все еще есть смысл, просто чтобы сделать код более перспективным. Что вы думаете?
Добавлено: Я думаю, что это не дублирует >> этот вопрос << - Я понимаю разницу между UT и TDD. Мой вопрос не о различиях , а о смысле написания юнит-тестов без TDD.
unit-testing
tdd
ex3v
источник
источник
Ответы:
TDD используется главным образом (1) для обеспечения покрытия, (2) и для обеспечения поддерживаемой, понятной, проверяемой конструкции. Если вы не используете TDD, вы не получите гарантированное покрытие кода. Но это ни в коем случае не означает, что вы должны отказаться от этой цели и беспечно жить с 0% охватом.
Регрессионные тесты были изобретены по причине. Причина в том, что в долгосрочной перспективе они экономят больше времени на предотвращенных ошибках, чем при написании дополнительных усилий. Это было доказано снова и снова. Поэтому, если вы не серьезно убеждены , что ваша организация гораздо лучше в разработке программного обеспечения , чем все гуру , которые рекомендуют регрессионное тестирование (или , если вы планируете идти вниз очень скоро , так что не не долго работать для вас), да, должны иметь модульные тесты, по той причине, которая применима практически ко всем другим организациям в мире: потому что они обнаруживают ошибки раньше, чем интеграционные тесты, и это сэкономит вам деньги. Не писать их - все равно что тратить свободные деньги, просто валяться на улице.
источник
У меня есть соответствующий анекдот от того, что происходит сейчас для меня. Я нахожусь на проекте, который не использует TDD. Наши специалисты по контролю качества двигают нас в этом направлении, но мы - небольшой наряд, и это был долгий, затяжной процесс.
В любом случае , я недавно использовал стороннюю библиотеку для выполнения конкретной задачи. Возникла проблема, связанная с использованием этой библиотеки, поэтому мне было поручено написать версию этой же библиотеки самостоятельно. В итоге это составило около 5000 строк исполняемого кода и около 2 месяцев моего времени. Я знаю, что строки кода - плохой показатель, но для этого ответа я считаю, что это достойный показатель величины.
Мне нужна была одна конкретная структура данных, которая позволяла бы мне отслеживать произвольное количество битов. Поскольку проект написан на Java, я выбрал Java
BitSet
и немного его изменил (мне также требовалась возможность отслеживать ведущие0
, чего Java BitSet по какой-то причине не делает .....). После достижения охвата ~ 93% я начал писать некоторые тесты, которые фактически подчеркивали бы написанную мной систему. Мне нужно было сравнить некоторые аспекты функциональности, чтобы убедиться, что они будут достаточно быстрыми для моих конечных требований. Неудивительно, что одна из функций, которые я переопределил вBitSet
интерфейсе, была абсурдно медленной при работе с большими наборами битов (в данном случае это сотни миллионов битов). Другие переопределенные функции основывались на этой единственной функции, поэтому это была огромная горлышко бутылки.В итоге я пошел к чертежной доске и нашел способ манипулировать базовой структурой
BitSet
, которая являетсяlong[]
. Я разработал алгоритм, попросил коллег ввести их, а затем приступил к написанию кода. Затем я запустил юнит-тесты. Некоторые из них сломались, и те, которые действительно указали мне, где я должен был искать в своем алгоритме, чтобы исправить это. После исправления всех ошибок модульных тестов я смог сказать, что функция работает как надо. По крайней мере, я мог быть уверен, что этот новый алгоритм работает так же, как и предыдущий алгоритм.Конечно, это не пуленепробиваемое. Если в моем коде есть ошибка, которую не проверяют юнит-тесты, я не буду ее знать. Но, конечно, та же самая ошибка могла быть и в моем более медленном алгоритме. Однако я могу с высокой степенью уверенности сказать, что мне не нужно беспокоиться о неправильном выводе этой конкретной функции. Предшествующие модульные тесты сэкономили мне часы, а может и дни, на попытки протестировать новый алгоритм, чтобы убедиться, что он правильный.
В этом и заключается смысл проведения модульных тестов независимо от TDD, то есть модульные тесты будут делать это для вас в TDD и за пределами TDD одинаково, когда вы закончите рефакторинг / сопровождение кода. Конечно, это должно сочетаться с регулярным регрессионным тестированием, тестированием дыма, нечетким тестированием и т. Д., Но модульное тестирование, как следует из названия, тестирует вещи на минимально возможном атомарном уровне, что дает вам направление, в котором появляются ошибки.
В моем случае без существующих модульных тестов мне как-то пришлось бы придумать метод, обеспечивающий постоянную работу алгоритма. Что, в конце концов ... звучит очень похоже на юнит-тестирование , не так ли?
источник
Вы можете разбить код примерно на 4 категории:
Модульные тесты становятся более ценными (вероятно, для выявления важных ошибок) по мере продвижения по списку. В моих личных проектах я почти всегда делаю TDD в категории 4. В категории 3 я обычно делаю TDD, если ручное тестирование не является более простым и быстрым. Например, код сглаживания будет сложным для написания, но гораздо проще проверить визуально, чем писать модульный тест, поэтому модульный тест будет стоить мне, только если этот код часто меняется. Остальную часть моего кода я проверяю только после того, как обнаружу ошибку в этой функции.
Иногда сложно заранее узнать, к какой категории относится определенный блок кода. Ценность TDD в том, что вы случайно не пропускаете ни одного из сложных модульных тестов. Стоимость TDD - это все время, которое вы тратите на написание простых модульных тестов. Однако обычно люди, имеющие опыт работы с проектом, с достаточной степенью уверенности знают, к какой категории относятся различные части кода. Если вы не занимаетесь TDD, вы должны хотя бы попытаться написать наиболее ценные тесты.
источник
Будь то юнит, компонент, интеграционные или приемочные испытания, важная часть - это то, что он должен быть автоматизирован Отсутствие автоматических тестов является фатальной ошибкой для любого программного обеспечения, от простых CRUD до самых сложных вычислений. Причина заключается в том, что написание автоматических тестов всегда будет стоить меньше, чем постоянная потребность в запуске всех тестов вручную, когда вы этого не делаете, на порядки. После того, как вы их написали, вам просто нужно нажать кнопку, чтобы посмотреть, пройдут они или нет. Ручные тесты всегда будут длиться долго и зависят от людей (живых существ, которым скучно, может не хватать внимания и т. Д.), Чтобы иметь возможность проверить, пройдены ли тесты успешно или нет. Короче говоря, всегда пишите автоматизированные тесты.
Теперь о причине, по которой ваш коллега может быть против проведения любого вида модульного тестирования без TDD: возможно, потому, что сложнее доверять тестам, написанным после производственного кода. И если вы не можете доверять своим автоматизированным тестам, они ничего не стоят . После цикла TDD вы должны сначала сделать тест неудачным (по правильной причине), чтобы иметь возможность написать производственный код, чтобы он прошел (и не более). Этот процесс по сути проверяет ваши тесты, поэтому вы можете им доверять. Не говоря уже о том, что написание тестов перед фактическим кодом подталкивает вас к разработке модулей и компонентов, чтобы их было легче тестировать (высокий уровень развязки, применение SRP и т. Д.). Хотя, конечно, выполнение TDD требует дисциплины .
Вместо этого, если вы сначала напишите весь производственный код, когда вы напишете тесты для него, вы ожидаете, что они пройдут при первом запуске. Это очень проблематично, потому что вы, возможно, создали тест, который покрывает 100% вашего производственного кода, не утверждая правильное поведение (может даже не выполнить никаких утверждений! Я видел, как это произошло ), так как вы не видите, что он не работает Сначала проверьте, если это не удается по правильной причине. Таким образом, у вас могут быть ложные срабатывания. Ложные срабатывания в конечном итоге подорвут доверие к вашему набору тестов, по сути, заставив людей снова прибегнуть к ручному тестированию, поэтому у вас будет стоимость обоих процессов (написание тестов + ручные тесты).
Это означает, что вы должны найти другой способ проверить свои тесты , как это делает TDD. Поэтому вы прибегаете к отладке, комментированию частей производственного кода и т. Д., Чтобы иметь возможность доверять тестам. Проблема в том, что процесс «тестирования ваших тестов» намного медленнее. Добавление этого времени к тому времени, которое вы потратите на запуск специальных тестов вручную (поскольку у вас нет автоматических тестов, пока вы кодируете рабочий код), по моему опыту, приводит к общему процессу, который намного медленнее, чем практика ТДД "По книге" (Кент Бек - ТДД по примеру). Кроме того, я готов сделать ставку и сказать, что «тестирование ваших тестов» после их написания требует гораздо больше дисциплины, чем TDD.
Так что, возможно, ваша команда может пересмотреть "деловые и некоммерческие причины", чтобы не заниматься TDD. По моему опыту, люди склонны думать, что TDD медленнее по сравнению с простым написанием модульных тестов после выполнения кода. Это предположение неверно, как вы прочитали выше.
источник
Часто качество тестов зависит от их происхождения. Я регулярно виновен в том, что не выполняю «настоящий» TDD - я пишу некоторый код, чтобы доказать, что стратегия, которую я хотел бы использовать, действительно работает, затем покрываю каждый случай, когда этот код должен поддерживать тесты после этого. Обычно единица кода - это класс, чтобы дать вам общее представление о том, сколько работы я с удовольствием выполню без покрытия тестами, то есть не большой суммы. Это означает, что семантическое значение тестов хорошо согласуется с тестируемой системой в ее «готовом» состоянии - потому что я написал их, зная, какие случаи выполняет SUT и как он их выполняет.
И наоборот, TDD с его политикой агрессивного рефакторинга имеет тенденцию к устареванию тестов, по крайней мере, так же быстро, как вы можете записать их, как открытый интерфейс изменений тестируемой системы. Лично я считаю, что умственная нагрузка как на разработку функциональных блоков приложения, так и на поддержание синхронности семантики тестов, которые его покрывают, слишком высока для поддержания моей концентрации, а обслуживание тестов часто падает. Кодовая база заканчивается тестами, которые не проверяют ничего ценного или просто ошибочны. Если у вас есть дисциплина и умственные способности, чтобы поддерживать тестовый комплект в актуальном состоянии, во что бы то ни стало, практикуйте TDD так строго, как вам бы того хотелось. Я не, поэтому я нашел это менее успешным по этой причине.
источник
На самом деле дядя Боб упомянул очень интересный момент в одном из своих видео «Чистые кодеры». Он сказал, что цикл Red-Green-Refactor можно применять двумя способами.
1-й - это обычный способ TDD. Напишите провальный тест, затем пройдите тест и, наконец, выполните рефакторинг.
2-й способ - написать очень маленький фрагмент производственного кода и сразу же выполнить его путем модульного тестирования, а затем рефакторинга.
Идея состоит в том, чтобы идти очень маленькими шагами. Конечно, вы теряете из производственного кода подтверждение того, что ваш тест перешел с красного на зеленый, но в некоторых случаях, когда я работал в основном с младшими разработчиками, которые отказывались даже пытаться понять TDD, это оказалось несколько эффективным.
Я снова повторяю (и это подчеркнул дядя Боб), что идея состоит в том, чтобы пойти очень маленькими шагами и немедленно протестировать только что добавленный фрагмент производственного кода.
источник