В разработке инженерного программного обеспечения обеспечение комплексного тестирования имеет решающее значение для надежности, безопасности и соответствия нормативным требованиям. Инструменты покрытия кода необходимы для выявления непроверенных путей в вашей кодовой базе - конкретных последовательностей кода, которые никогда не выполняются во время вашего набора тестов. Систематически обнаруживая эти пробелы, инженерные команды могут уменьшить скрытые ошибки, повысить надежность программного обеспечения и соответствовать строгим отраслевым стандартам, таким как DO-178C (авионика) или ISO 26262 (автомобильный). В этой статье исследуется, как работают инструменты покрытия кода, типы покрытия, которые они предоставляют, и как использовать их для поиска и устранения непроверенных путей.

Что такое инструменты покрытия кода?

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

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

Инструменты покрытия поддерживают несколько языков и платформ. Для инженерного программного обеспечения, написанного на C/C++, распространены такие инструменты, как gcov и BullseyeCoverage. Для систем на базе Java JaCoCo является стандартом де-факто. Для .NET широко используются OpenCover или Coverlet. Независимо от экосистемы принцип остается прежним: измерять то, что тестируется, затем фокусировать усилия на том, что не является.

Виды покрытия и их значение

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

Покрытие линии

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

Охват филиалов

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

Покрытие пути

Покрытие пути является наиболее полным, но также и наиболее трудным для достижения. Для проверки каждой возможной уникальной траектории выполнения через функцию или модуль. Для функции с несколькими вложенными условиями число путей растет экспоненциально (взрыв пути). На практике покрытие пути часто аппроксимируется путем объединения покрытия ветви и состояния. Критически важные стандарты, такие как уровень DO-178C A, могут потребовать модифицированного покрытия состояния / решения (MC / DC) в качестве практической замены, где каждое условие в решении показано независимо влиять на результат.

Покрытие условий (MC/DC)

Покрытие условий гарантирует, что каждое Булево подвыражение (условие) в решении было оценено как истинно, так и ложно. MC/DC идет дальше, требуя, чтобы каждое условие независимо меняло результат решения. Это самая строгая форма покрытия для критически важного для безопасности инженерного программного обеспечения и непосредственно раскрывает непроверенные пути через сложную логику. Например, в системе управления полетом авионики анализ MC/DC может показать, что конкретное состояние неисправности датчика никогда не вызывает отключение системы, потому что набор тестов никогда не осуществлял эту комбинацию входов.

Понимание этих типов покрытия позволяет командам выбрать правильную метрику для своего уровня сертификации и профиля риска.Для систем высокой целостности опасно полагаться исключительно на покрытие линий; непроверенные ветви и пути могут привести к катастрофическим сбоям.

Зачем искать непроверенные пути?

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

  • Therac-25 (1980-е): Машина лучевой терапии вышла из строя из-за состояния гонки в своем программном обеспечении управления, которое никогда не тестировалось под определенными операционными последовательностями. Кодовый путь, который позволил ошибку, был обнаружен тестированием, но только после смертельной аварии.
  • Mars Climate Orbiter (1999):] Путь навигационного кода, который смешивал метрические и имперские единицы, никогда не использовался в наземных испытаниях.
  • Toyota unintended acceleration (2009): Критические кодовые пути в ECU не тестировались в реальных условиях, что привело к отзыву миллионов автомобилей.

Выявление непроверенных путей до выпуска является проактивной стратегией снижения рисков. Это также помогает удовлетворить аудиторов регулирующих органов: такие стандарты, как ISO 26262, DO-178C и IEC 62304, требуют структурного анализа покрытия в рамках процесса проверки. Используя инструменты покрытия для поиска непроверенных путей, команды могут документировать соответствие и укреплять доверие к своему программному обеспечению.

Использование инструментов покрытия для поиска непроверенных путей

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

Измерение и испытание

Сначала компилируйте или запустите свой код с включенным прибором покрытия. Для gcov компилируйте с . Для JaCoCo используйте агента через флаг . Запустите полный набор тестов. Инструмент записывает, какой код нажимается, и записывает необработанные файлы данных (например, для gcov, для JaCoCo).

Создание и обзор отчетов по охвату

Используйте команду отчетности инструмента (например, , ) для создания отчетов HTML или XML. Эти отчеты представляют собой строки цветового кода (зеленый = хит, красный = не хит) и список раскрытых ветвей. Сначала сосредоточьтесь на функциях или модулях с низким процентом покрытия. Для каждой раскрытой строки или ветви спросите: доступен ли этот код при любых условиях? Если да, он представляет собой непроверенный путь.

Анализ непроверенных путей

Не все непроверенные пути одинаково важны.

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

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

Популярные инструменты покрытия для инженерного программного обеспечения

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

gcov (C/C++)

gcov - это инструмент покрытия GNU, в комплекте с GCC. Он обеспечивает покрытие линий и ветвей и является бесплатным и открытым исходным кодом. Он хорошо интегрируется с системами сборки, такими как CMake, и может использоваться в кросс-компиляции для встроенных целей. Официальная документация gcov объясняет, как генерировать отчеты.

JaCoCo (Ява)

JaCoCo - это отраслевая стандартная библиотека охвата для Java. Она предлагает покрытие линий, филиалов и методов. Ее агент может подключаться к запуску JVM без изменений кода. Для инженерных систем, построенных на Java (например, SCADA или фреймворки моделирования), JaCoCo очень эффективен. На веб-сайте JaCoCo есть подробные руководства по конфигурации.

BullseyeCoverage (C/C++)

BullseyeCoverage - это коммерческий инструмент, который обеспечивает покрытие функций, ветвей и условий (включая MC/DC). Он предназначен для критически важной для безопасности и встроенной разработки. Его отчеты показывают, какие условия в выражениях не проверены. Многие команды в аэрокосмической и автомобильной промышленности полагаются на BullseyeCoverage для соответствия DO-178C и ISO 26262.

Другие инструменты

  • OpenCppCoverage (Windows, C/C++) — бесплатный инструмент с открытым исходным кодом, который интегрируется с Visual Studio и предлагает покрытие филиала.
  • Coverage.py (Python) — для инженерных скриптов, основанных на данных, написанных на Python, этот инструмент обеспечивает покрытие линий и ветвей.
  • Встроенный охват Go — Go обеспечивает покрытие линий и заявлений с экспериментальной поддержкой филиала.

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

Интеграция охвата в рабочий процесс развития

Выявление непроверенных путей должно быть непрерывной деятельностью, а не одноразовым аудитом. Встраиваем анализ покрытия в ваш конвейер CI/CD:

  • Охват каждого обязательства — даже частичное покрытие дает быструю обратную связь.
  • Установите минимальные пороги покрытия — неисправные сборки, если покрытие падает ниже настраиваемого уровня (например, 80% охвата ветви для критических модулей).
  • Создавайте отчеты о покрытии в виде артефактов — делайте их доступными для всех разработчиков.
  • Создать диффы покрытия — инструменты, такие как Codecov, показывают, какие линии новых касаний запроса на вытягивание, которые не проверены, заставляя разработчиков добавлять тесты для необнаруженных изменений.
  • Слияние врат на непроверенных путях — для высокоцелевого программного обеспечения перед слиянием требуется 100% покрытие MC/DC для критически важных функций безопасности.

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

Лучшие практики для эффективного использования

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

  • Объедините несколько типов покрытия — только покрытие линии может быть обманчивым. Используйте покрытие ветви и состояния, чтобы раскрыть более глубокие непроверенные пути.
  • Сосредоточьтесь на коде высокого риска — целевых сложных функциях, обработчиках ошибок и чувствительных к безопасности процедурах. Не весь код нуждается в 100% покрытии; сосредоточьтесь на том, что имеет значение.
  • Использовать статический анализ наряду с покрытием — статический анализ может найти недостижимый код, который могут пропустить инструменты покрытия (например, мертвый код, не выполненный из-за логических ошибок).
  • Охват измерениями в реалистичных условиях — использование системных и интеграционных тестов, а не только единичных тестов.Непроверенные пути часто лежат на границах между модулями.
  • Избегать покрытия ради покрытия — писать тесты, которые искусственно повышают охват без проверки поведения (например, тестирование тривиальных геттеров / монтажников) тратит усилия.
  • Обзор тенденций охвата с течением времени — тенденция снижения охвата указывает на то, что новый код добавляется без соответствующих тестов, создавая новые непроверенные пути.
  • Образовать команду — помочь разработчикам понять, что отчеты о покрытии — это не суждение, а инструмент для поиска пробелов.

Проблемы и ограничения

Хотя мощные, инструменты покрытия кода имеют ограничения, которые команды должны признать:

  • Накладные расходы — приборы могут замедлить выполнение теста и увеличить двоичный размер. Для встроенных систем с плотной памятью это может быть проблематично. Накладные расходы на время выполнения часто минимальны, но требуют тщательной настройки.
  • Ложная уверенность — высокий охват не означает идеальное тестирование. Тесты могут выполнять код, но не проверять результаты должным образом. Объедините охват с плотностью утверждения и тестированием на мутации.
  • Путь взрыва — для очень сложного кода со многими условиями покрытие пути вычислительно неосуществимо.Использовать MC/DC или покрытие ветви в качестве практической замены.
  • Инструментация в производстве — большинство инструментов покрытия предназначены для тестирования разработки.Внедрение инструментального кода в производство является рискованным из-за проблем производительности и безопасности.
  • Языковые ограничения и ограничения окружающей среды — некоторые встроенные цели не имеют надежного инструментария покрытия, особенно для сборки или пользовательского оборудования.

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

Заключение

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