Почему провал модульных тестов считается плохим?

93

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

Лично я думаю, что это не так, как должно быть по следующим причинам:

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

  2. Недопустимо придумывать модульные тесты, которые не пройдут. Или, конечно, придумать модульные тесты, которые было бы сложно исправить.

  3. Если в какой-то момент времени все модульные тесты пройдут, то в любой момент времени не будет общей картины о состоянии программного обеспечения. Здесь нет дорожной карты / цели.

  4. Это препятствует написанию модульных тестов заранее - до реализации.

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

Я что-то здесь упускаю? Почему организации ожидают, что все модульные тесты пройдут? Разве это не жизнь в мире снов? И разве это не сдерживает реальное понимание кода?

user619818
источник
Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
maple_shaft

Ответы:

270

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

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

В промежуточной или выпускной ветке неудачные тесты являются «красным предупреждением», показывающим, что с каким-то набором изменений было что-то совершенно не так, когда он был объединен из магистрали в ветку выпуска.

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

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

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

Док Браун
источник
18
Я думаю, что это настоящий ответ. ОП упоминает «процесс выпуска» и «некоторый экран [показывающий результаты теста]», который звучит как сервер сборки. Релиз - это не то же самое, что разработка (не развиваться в производстве!); хорошо иметь провальные тесты в dev, они как TODO; все они должны быть зелеными (ВЫПОЛНЕНО) при отправке на сервер сборки.
Warbo
7
Гораздо лучший ответ, чем самый высокий голос. Он показывает понимание того, откуда происходит операция, не читая им лекций о некой идеальной ситуации в мире, признает возможность известных ошибок (для которых не вся дорожная карта отбрасывается, чтобы исправить какой-то редкий угловой случай) и объясняет, что модульные тесты должны определенно быть зеленым в ветке / процессе релиза.
Себастьян ван ден Брук
5
@SebastiaanvandenBroek: спасибо за ваш положительный ответ. Просто чтобы прояснить это: ИМХО провальные юнит-тесты должны быть редкими даже в транке, так как слишком частое получение таких отказов будет беспокоить всю команду, а не только того, кто внес изменения, вызвавшие сбой.
Док Браун
4
Я думаю, что проблема здесь в том, что все автоматизированные тесты являются модульными. Многие тестовые среды включают возможность помечать тесты, которые, как ожидается, не пройдут (часто это называется XFAIL). (Это отличается от теста, который требует результата ошибки. В идеале тесты XFAIL будут успешными, но не будут.) Набор тестов все еще проходит с этими ошибками. Наиболее распространенный вариант использования - это то, что происходит только на некоторых платформах (и только на них XFAIL), но использование функции для отслеживания чего-либо, что потребует слишком много работы для исправления прямо сейчас, также в пределах разумного. Но такие тесты обычно не являются юнит-тестами.
Кевин Кэткарт
1
+1, хотя я предлагаю небольшое дополнение (выделено жирным шрифтом) к этому предложению: «Это становится громоздким, подверженным ошибкам, заставляет людей игнорировать сбои в наборе тестов как шум и отбрасывает огромную часть аспекта автоматизации модульных тестов .
mtraceur
228

... все юнит-тесты пройдены зеленым цветом - что должно быть хорошо.

Это является хорошим. Нет, «должно быть» об этом.

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

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

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

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

Чтобы сравнить образ мыслей разработчика и тестера:

  • Разработчик останавливается, как только код делает то, что он хочет.
  • Тестер останавливается, когда они больше не могут нарушить работу кода.

Это радикально разные точки зрения, и многим разработчикам трудно их согласовать.

Или, конечно, придумать модульные тесты, которые было бы сложно исправить.

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

  • Отладка «доказывает», что код делает то, что вы хотите сегодня .
  • Тесты «доказывают», что код все еще делает то, что вы хотите, с течением времени .

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

Единственное «графическое» тестирование дает вам снимок того, что код «работает» в тот момент, когда он был протестирован. Как это развивается после этого - другая история.

Это препятствует написанию модульных тестов заранее - до реализации.

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

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

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

Разве это не жизнь в мире снов?

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

И разве это не сдерживает реальное понимание кода?

Может быть. Вам не обязательно понимать реализацию чего-либо, чтобы писать тесты для него (это часть их смысла). Тесты определяют поведение и ограничения приложения и гарантируют, что они останутся прежними, если вы не измените их намеренно.

Фил В.
источник
7
@ Tibos: отключение теста похоже на комментирование функции. У вас есть контроль версий. Используй это.
Кевин
6
@Kevin Я не знаю, что ты имеешь в виду под «использовать это». Я отмечаю тест как «пропущенный» или «ожидающий» или любое другое соглашение, которое использует мой организатор тестов, и фиксирую этот тег пропуска в управлении версиями.
августа
4
@dcorking: я имею в виду, не комментируйте код, удалите его. Если позже вы решите, что вам это нужно, восстановите его из-под контроля версий. Фиксация отключенного теста ничем не отличается.
Кевин
4
«Вполне возможно, что ваши тесты не охватывают каждый случай». Я бы зашел так далеко, чтобы сказать, что для каждого нетривиального фрагмента кода, который вы тестируете, вы определенно не охватите все случаи.
CorsiKa
6
@ Tibos Сторонники модульного тестирования говорят, что время цикла от написания неудачного теста до написания кода для него должно быть небольшим (например, 20 минут. Некоторые утверждают, что 30 секунд). Если у вас нет времени, чтобы написать код сразу, возможно, он слишком сложен. Если это не сложно, удалите тест, так как он может быть переписан, если добавленная функция будет добавлена ​​снова. Почему бы не прокомментировать это? Вы не знаете, что эта функция будет добавлена ​​снова, поэтому закомментированный тест (или код) - это просто шум.
CJ Деннис
32

Почему провал модульных тестов считается плохим?

Это не так - разработка, основанная на тестировании, основана на идее провала тестов. Неудачные юнит-тесты для запуска разработки, неудачные приемочные тесты для запуска истории ....

То, что вам не хватает, это контекст ; где модульные тесты могут провалиться?

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

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

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

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

Чтобы быть более точным: дисциплина состоит в том, что тесты, которые выполняются во время сборки, должны пройти.

Насколько я могу судить, нет ничего плохого в том, что неудачные тесты отключены .

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

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

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

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

VoiceOfUnreason
источник
Я бы сказал : «Там нет ... ничего плохого в том , провал попытки тестов , которые являются инвалидами ».
CJ Деннис
Это изменение, безусловно, проясняет смысл. Спасибо.
VoiceOfUnreason
26

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

Модульные тесты не для проверки того, что ваш код не содержит ошибок.

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

Модульные тесты проверяют, что ваш код делает то, что вы думаете.

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

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

Приемочные тесты могут быть тем, что вы ищете.

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

Frax
источник
2
Однажды мне пришлось заменить библиотеку на другую. Модульные тесты помогли мне убедиться, что новый код по-прежнему обрабатывает все угловые случаи одинаково.
Турбьерн Равн Андерсен
24

Я рассматриваю это как программный эквивалент синдрома разбитого окна .

Рабочие тесты говорят мне, что код имеет заданное качество и что владельцы кода заботятся об этом.

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

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

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

Робби Ди
источник
9
Если тесты документируют, как работает система, они, безусловно, всегда должны проходить - если нет, это означает, что инварианты нарушены. Но если они документируют то, как система должна быть, неудачные тесты также могут быть использованы - при условии, что ваша инфраструктура модульного тестирования поддерживает хороший способ пометить их как «известные проблемы», и если вы связываете их с элементом в вашем трекере. Я думаю, что оба подхода имеют свои достоинства.
Луаан
1
@Luaan Да, это скорее предполагает, что все модульные тесты созданы одинаково. Разумеется, что менеджеры по сборке нередко нарезают и нарезают тесты по какому-либо атрибуту в зависимости от того, как долго они выполняются, насколько они хрупкие и по различным другим критериям.
Робби Ди
Этот ответ очень хорош по моему собственному опыту. Как только некоторые люди привыкли игнорировать кучу неудачных тестов или нарушать лучшие практики в некоторых моментах, подождите пару месяцев, и вы увидите, что процент игнорируемых тестов резко возрастает, а качество кода падает до уровня «хак-сценария» , И будет очень сложно отозвать всех к процессу.
usr-local-ΕΨΗΕΛΩΝ
11

Вот основная логическая ошибка:

Если это хорошо, когда все тесты пройдены, то должно быть плохо, если какие-либо тесты не пройдены

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

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

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

Ответ Фила У великолепен. Я не могу заменить это.

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

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

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

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


Это может создать конфликты в отношении правил команды. Я действительно столкнулся с этим несколько недель назад:

  • Каждый коммит / толчок вызывает сборку. Сборка никогда не должна давать сбои (если она сработает или какой-либо тест не пройден, виновный разработчик обвиняется).
  • Ожидается, что каждый разработчик внесет свои изменения (даже если они будут неполными) в конце дня, поэтому руководители группы могут проверять код утром.

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

В этом сценарии проблема не в том, что модульные тесты не имеют смысла; дело в том, что у компании нереальные ожидания . Их произвольный набор правил не охватывает все случаи, и несоблюдение правил слепо расценивается как сбой разработчика, а не как сбой правила (как, впрочем, и в моем случае).

Flater
источник
3
Единственный способ, которым это может сработать, - это использовать ветвление, так что разработчики фиксируют и отправляют в ветки объектов, которые не нуждаются в чистом построении, хотя они и не завершены, но коммиты в основную ветвь действительно вызывают сборку, которая должна собираться чисто.
Гвин Эванс
1
Принудительное продвижение неполных изменений абсурдно, я не вижу никакого оправдания для этого. Почему бы не пересмотреть код после завершения изменения?
Каллум Брэдбери
Ну, во-первых, это быстрый способ убедиться, что код находится не только на ноутбуке / рабочей станции разработчика, если их жесткий диск перестал работать или был потерян, - если есть политика фиксации, даже если в середине работы, то есть ограниченный объем работы на риск.
Гвин Эванс
1
Флажки функций фиксируют очевидный парадокс.
RubberDuck
1
@Flater да, для переделки существующей логики тоже.
RubberDuck
6

Если вы не исправите все модульные тесты, вы можете быстро попасть в состояние, когда никто не исправляет какие-либо сломанные тесты.

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

  2. Это мешает создавать код, который тоже будет сложно тестировать, что хорошо с точки зрения дизайна

  3. Покрытие кода может помочь там (хотя это не панацея). Также модульные тесты - это только один из аспектов тестирования - вам также нужны интеграционные / приемочные тесты.

JK.
источник
6

Чтобы добавить несколько пунктов к уже хорошим ответам ...

но в любой момент все модульные тесты должны пройти

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

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

Другие ответы охватили пределы тестирования.

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

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

Почему должна быть дорожная карта?

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

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

Грэхем
источник
6

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

Не правда. почему вы думаете, что это невозможно? Вот пример для программы, которая работает:

public class MyProgram {
  public boolean alwaysTrue() {
    return true;
  }

  @Test
  public void testAlwaysTrue() {
    assert(alwaysTrue() == true);
  }
}

Недопустимо придумывать модульные тесты, которые не пройдут. Или, конечно, придумать модульные тесты, которые было бы сложно исправить.

В этом случае это может быть не модульный тест, а интеграционный тест, если он сложный

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

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

Это препятствует написанию модульных тестов заранее - до реализации.

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

user7294900
источник
«Разработчики будут удерживать [sic] от написания любых тестов по своей природе» - это полная чушь. Я работаю в целой компании разработчиков, которые практикуют TDD и BDD.
RubberDuck
@RubberDuck Я пытался ответить на «факт» в вопросе, и я преувеличивал. Я
обновлю
«X будет удерживаться от выполнения Y, если они не понимают преимуществ Y», относится практически к любым X и Y, так что это утверждение, вероятно, не особенно полезно. Вероятно, было бы более разумно объяснить преимущества написания тестов и, в частности, сделать это заранее.
Dukeling
2
«невозможно для программы любого размера» не означает «все программы, независимо от размера», это означает «любую значимую программу (имеющую нетривиальную длину)». Ваш предпринятый контрпример не применим, потому что он не т значительная и полезная программа.
Бен Фойгт
@ BenVoigt Я не думаю, что я должен дать "значительную программу" в качестве ответа.
user7294900
4

Это продвигает идею, что код должен быть идеальным и не должно быть никаких ошибок

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

Недопустимо придумывать модульные тесты, которые не пройдут. Или, конечно, придумать модульные тесты, которые было бы сложно исправить.

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

  • Если тест не пройден, вы не знаете, действительно ли код тестируется тестом. Эти два, возможно, не связаны вообще.
  • Изменив код так, чтобы тест превратился из красного в зеленый, не более и не менее, вы можете быть достаточно уверены, что ваш код выполняет то, что он должен делать, и не намного (что вам никогда не понадобится).

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

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

Функция тестов не должна быть завершена до того, как будет написан код.

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

Это препятствует написанию модульных тестов заранее - до реализации.

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

Я что-то здесь упускаю? Почему организации ожидают, что все модульные тесты пройдут?

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

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

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

Разве это не жизнь в мире снов?

Нет. Работа с приложением, управляемым тестами, - это чистая радость, если только вам не нравится концепция по какой-либо причине («больше усилий» и т. Д.), Которую мы можем обсудить в другом вопросе.

И разве это не сдерживает реальное понимание кода?

Абсолютно нет, с чего бы это?

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

Очевидно, что писать плохие тесты - это плохо. Но это не имеет ничего общего с функцией тестов как таковых.

Anoe
источник
3

(Из моих оригинальных комментариев)

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

В качестве упражнения попробуйте заменить фразу «модульный тест» в своем вопросе на «ошибка компилятора» (или «ошибка синтаксиса», если компилятор отсутствует). Очевидно, что в выпуске не должно быть ошибок компилятора, так как он был бы непригодным для использования; все же ошибки компилятора и синтаксические ошибки являются нормальным состоянием на компьютере разработчика, когда они пишут код. Ошибки исчезают только после их завершения; и это именно то, когда код должен быть передан. Теперь замените «ошибка компилятора» в этом абзаце на «модульный тест» :)

Warbo
источник
2

Цель автоматизированных тестов - сообщить вам, когда вы что-то сломали, как можно раньше . Рабочий процесс выглядит примерно так:

  1. Изменить
  2. Создайте и проверьте свои изменения (в идеале автоматически)
  3. Если тесты не пройдены, это означает, что вы сломали что-то, что раньше работало
  4. если тесты пройдены, вы должны быть уверены, что ваши изменения не привели к новым регрессиям (в зависимости от охвата тестами)

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

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

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

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

Это сдерживает написание модульных тестов заранее

Если это работает для вас, тогда пишите тесты заранее, просто не проверяйте их в своем мастере / транке, пока они не пройдут.

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

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

Джастин
источник
2

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

в любой момент времени все модульные тесты должны пройти

Нет. Конечно, это не будет правдой. Во время разработки программного обеспечения NCrunch чаще всего либо коричневого цвета (сбой сборки), либо красного цвета (неудачный тест).

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

Это также относится к теме создания новых тестов: тесты должны утверждать логику и поведение кода. Граничные условия, неисправные состояния и т. Д. Когда я пишу новые тесты, я пытаюсь определить эти «горячие точки» в коде.

Модульные тесты документируют, как я ожидаю, что мой код будет вызываться - предварительные условия, ожидаемые результаты и т. Д.

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


Напомним, что модульное тестирование иногда идет рука об руку с разработкой, управляемой тестами. Одним из принципов TDD является то, что сломанные тесты - ваши ориентиры. Если тест не пройден, вам нужно исправить код, чтобы тест прошел. Вот конкретный пример с этой недели:

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

Вместо этого моя библиотека анализирует запрос, используя Antlr и синтаксис Oracle 12c, а затем помещает различные утверждения в само дерево синтаксиса. Такие вещи, как, он действителен (ошибки синтаксического анализа не возникали), все его параметры удовлетворяются набором параметров, все ожидаемые столбцы, считываемые средством чтения данных, присутствуют в запросе и т. Д. Все это элементы, которые проскользнули в производство в разное время.

Один из моих коллег-инженеров прислал мне запрос в понедельник, который потерпел неудачу (или, скорее, был успешным, когда он должен был выйти из строя) в выходные. Моя библиотека сказала, что синтаксис в порядке, но он взорвался, когда сервер попытался запустить его. И когда он посмотрел на запрос, было очевидно, почему:

UPDATE my_table(
SET column_1 = 'MyValue'
WHERE id_column = 123;

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

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

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

GalacticCowboy
источник
0

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

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

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

Майкл Кей
источник
Я думаю, что то, что вы описываете как «внешние тесты», часто описывается как «интеграционные» тесты.
GalacticCowboy
Да, но я сталкивался с различиями в терминологии. Для некоторых людей интеграционное тестирование - это больше развернутая конфигурация программного / аппаратного обеспечения / сети, тогда как я говорю о внешнем поведении части программного обеспечения, которую вы разрабатываете.
Майкл Кей
0

«но в любой момент все юнит-тесты должны пройти»

Если это отношение в вашей компании, это проблема. В ОПРЕДЕЛЕННОЕ время, а именно, когда мы объявляем, что код готов к переходу в следующую среду, все модульные тесты должны пройти. Но во время разработки мы должны ожидать, что многие модульные тесты не пройдут.

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

«Недопустимо придумывать модульные тесты, которые не пройдут. Или, конечно, придумать модульные тесты, которые было бы сложно исправить». Если кто-то в вашей организации считает, что он не должен упоминать о возможном тесте, потому что он может провалиться и заставить его проделать дополнительную работу по его устранению, этот человек совершенно не годится для своей работы. Это катастрофическое отношение. Хотели бы вы, чтобы врач сказал: «Когда я делаю операцию, я сознательно не проверяю правильность швов, потому что, если я увижу их, мне придется вернуться и сделать их заново, и это замедлит завершение операции "?

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

Да, конечно, иногда рациональные люди говорят: «Мы приближаемся к крайнему сроку, это тривиальная проблема, и сейчас не стоит тратить ресурсы на то, чтобы ее исправить». Но вы не можете принять это решение рационально, если вы не знаете. Рационально изучить список ошибок и назначить приоритеты и графики их исправления. Умышленно делать себя неосведомленным о проблемах, чтобы вам не приходилось принимать это решение, глупо. Как вы думаете, клиент не узнает только потому, что вы не хотели знать?

сойка
источник
-7

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

Один известный пример этого происходит в игре 2,4,6.

  • У меня в голове есть правило, что любая серия из трех чисел пройдет или потерпит неудачу,
  • 2,4,6 это пропуск
  • Вы можете перечислить наборы из трех чисел, и я скажу вам, если они пройдут или не пройдут.

Большинство людей выбирают правило, например, «разрыв между 1-м и 2-м числом такой же, как разрыв между 2-м и 3-м».

Они будут проверять некоторые числа:

  • 4, 8, 12? Проходят
  • 20, 40, 60? Проходят
  • 2, 1004, 2006? Проходят

Они говорят: «Да, каждое наблюдение подтверждает мою гипотезу, это должно быть правдой». И объявить свое правило человеку, дающему загадку.

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

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

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

Скотт
источник
2
Какое отношение это имеет к вопросу? Неудачные юнит-тесты по определению свидетельствуют о проблеме.
Фракс
1
Вы абсолютно можете иметь модульные тесты, которые требуют, чтобы тестируемая система входила в режим отказа. Это не то же самое, что никогда не видеть провал теста. Это также, почему TDD указан как цикл «
Красный-