Эволюция программы и разбитые тесты

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

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

Я также хотел бы найти конкретные примеры неудачных тестов. Знаете ли вы о каких-либо приложениях с открытым исходным кодом, которые эволюционировали способами, которые привели к неудачам тестов?

Ответ 1

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

Верно. Совместная эволюция производственного кода и тестового кода вместе сложна. Оба типа кода разделяют сходство (например, соглашение об именах), но они по-прежнему отличаются по своей природе. Например, DRY может быть нарушен в тестовом коде, если это необходимо; кодовое дублирование действительно было бы легко найдено, так как тест разрывался в обоих случаях. Напряженность между тестовым и производственным кодами приводит иногда к определенному компромиссу конструкции (например, к инъекции зависимостей), чтобы облегчить тестируемость. Опять же, эта напряженность относительно новая, и связь между дизайном производственного кода и усилиями по техническому обслуживанию не совсем понятна. Статья " Взаимодействие между тестированием и эволюцией программного обеспечения" отлично (я не смог найти ее в PDF, но не сделал Google в течение длительного времени).

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

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

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

Как страх неудач влияет на ваше желание писать тесты?

Вам нужно найти баланс между (1) начальным временем, затраченным на тестирование в комплекте (2), а также (3) эффективностью набора тестов. Я пишу в основном то, что я называю "точками перегиба", и здесь мой взгляд по этому вопросу.

Ответ 2

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

Ответ 3

Как страх неудач влияет на ваше желание писать тесты?

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

Лично я не думаю, что этого можно избежать. Вы можете свести к минимуму эффекты за счет изоляции эффекта, но иногда это может быть довольно сложно. Мошки могут помочь, но с ними иногда трудно работать. Если поведение меняется, и изменение поведения было намеренным, и у вас есть Y-тесты, которые зависят от поведения, тогда только имеет смысл, что вам придется изменить все ожидания Y. Я обнаружил, что, выполняя немного ООП или просто подходящую конструкцию пакета, вы иногда можете немного сэкономить, воспользовавшись преимуществом повторного использования кода. Я никогда не боюсь провалов теста в этом отношении, если поведение должно измениться (и вы вложили мысль в эту потребность, необходимость реальна, и необходимость не в прихоти вашего менеджера, который использовал для написания COBOL в хорошем оле 'days:-), то это всего лишь часть разработки базы кода и должна рассматриваться как часть выполняемой работы. Если повторный фактор набора тестов занимает много времени, вы можете исключить тесты из своей сборки и повторно включать их по одному, но тесты, которые все еще проверяют ожидаемое поведение, не должны удаляться, а повторно учитываться. Не позволяйте тестовому набору разойтись, чтобы получить новую функцию, если это вообще можно избежать.

Ответ 7

Проблема, которую вы описываете, - это то, что лучше всего решать в начале проекта. С момента начала написания модульных тестов (желательно TDD-style) вы должны постоянно знать, как разворачиваться и развиваться.
Здесь нет волшебного трюка, вам нужно много думать и замечать, когда вы создаете запутанный беспорядок, который на самом деле затруднит проведение тестов. Обычно, если тесты настолько запутаны, это означает, что некоторые из производственного кода могут также использовать некоторые рефакторинги.

В основном, рефакторинг постоянно, чтобы убедиться, что изменения локализованы, и что вам не придется менять 200 тестовых случаев просто потому, что вы решили сделать простые изменения. Можно было бы сказать, что ваш код должен быть достаточно гранулированным, чтобы обеспечить легкое тестирование (нет необходимости заглушать десять объектов перед вызовом кода и его тестированием), что обычно упрощает его обслуживание через несколько месяцев.

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

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

Ответ 8

Сломанные тесты - большая проблема на практике. Я видел, как многие группы отказывались от агрессивного тестирования, потому что затраты на поддержание нарушенных тестов являются ошеломляющими. Вот связанный разговор, который может представлять интерес. https://sites.google.com/a/allegheny.edu/regression-2012/acknowledging-the-elephant-in-the-room-brittle-test-cases

Ответ 9

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

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

Ответ 10

Мой ответ на это для некоторого программного обеспечения, над которым я работал несколько лет назад, состоял в том, чтобы подготовить серию тестов и правильные ответы на те тесты на момент запуска. Поскольку изменения в программном обеспечении могут изменить вывод на другой (альтернативный) правильный вывод, я создал тестовую систему резервного вывода. Используя эту систему, я смог отслеживать изменения в тестовом выходе и фиксировать непреднамеренные ошибки и отслеживать, когда они произошли, используя программу различий, применяемую между текущим выходом и одним из прошлых выходов.
Мои тесты были разработаны, чтобы проходить через каждый основной цикл в программе с различными настроенными параметрами.
Это было до Windows-NT, так что все было сделано с пакетными файлами DOS и утилитами DOS.
Я также написал пакет который позволил мне выполнять программы, похожие на то, как работают UNIX find и xargs.

Ответ 11

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

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

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

Ответ 12

Вот мысленный эксперимент. Это выглядит только как напыщенная речь

Попытайтесь не выполнять тесты устройства.

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

(Кстати, этот тестовый режим имеет название: "Интеграция большого взрыва": когда вы пытаетесь в электронике, это буквально треск, так как все оборудование загорается.)

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

Посмотрите на их лицо. Для лучшего эффекта выберите кого-то, кто является лицензированным PE (профессиональным инженером) в вашей местности.

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

Повторите, опять же, замените оценщика CMMI. Этот последний будет забавным. Оценщики CMMI SCAMPI любят объяснять, как "это было слишком много усилий"

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

В этот момент проблема заключается в том, что средний магазин программного обеспечения, вероятно, вообще не имеет значения unit test.

Итак, соглашайтесь с усреднением и используйте установленные отраслевые худшие практики. Затем офшоринг в < insert страна дешевле, чем где вы живете > также не может иметь никакого влияния на качество (нет процесса хуже). Итак, на самом деле, ваш босс должен уйти с работы сейчас и сэкономить деньги. Там что-то не так с этим рассуждением.