В разработке программного обеспечения покрытие кода — это процентная мера степени выполнения исходного кода программы при запуске определенного набора тестов . В программе с высоким тестовым покрытием во время тестирования выполняется большая часть исходного кода, что предполагает меньшую вероятность наличия необнаруженных программных ошибок по сравнению с программой с низким тестовым покрытием. [1] [2] Для расчета тестового покрытия можно использовать множество различных показателей. Некоторые из самых основных — это процент программных подпрограмм и процент программных операторов , вызываемых во время выполнения набора тестов.
Тестовое покрытие было одним из первых методов систематического тестирования программного обеспечения . Первая опубликованная ссылка была сделана Миллером и Мэлони в журнале Communications of ACM в 1963 году. [3]
Чтобы измерить, какой процент кода был выполнен набором тестов , используется один или несколько критериев покрытия . Обычно они определяются как правила или требования, которым должен удовлетворять набор тестов. [4]
Существует ряд критериев покрытия, но основными из них являются: [5]
Например, рассмотрим следующую функцию C :
int foo ( int x , int y ) { int z знак равно 0 ; если (( x > 0 ) && ( y > 0 )) { z = x ; } Вернуть z ; }
Предположим, что эта функция является частью какой-то более крупной программы, и эта программа запускалась с каким-то набором тестов.
foo
была вызвана хотя бы один раз.foo(1,1)
, потому что в этом случае будет выполнена каждая строка функции, включая z = x;
.foo(1,1)
и foo(0,1)
потому, что в первом случае оба if
условия выполнены и z = x;
выполняются, а во втором случае первое условие (x>0)
не выполняется, что препятствует выполнению z = x;
.foo(1,0)
, foo(0,1)
и foo(1,1)
. Это необходимо, поскольку в первом случае (x>0)
оценивается как true
, а во втором — как false
. В то же время, первый случай делает (y>0)
false
, второй случай не оценивает (y>0)
(из-за ленивого вычисления логического оператора), третий случай делает это true
.В языках программирования, которые не выполняют короткую оценку , покрытие условий не обязательно подразумевает покрытие ветвей. Например, рассмотрим следующий фрагмент кода Pascal :
если а и б , то
Покрытие условий может быть выполнено с помощью двух тестов:
a=true
,b=false
a=false
,b=true
Однако этот набор тестов не удовлетворяет покрытию ветвей, поскольку ни один из случаев не соответствует условию if
.
Внедрение ошибок может потребоваться для обеспечения адекватного покрытия всех условий и ветвей кода обработки исключений во время тестирования.
Комбинацию покрытия функций и покрытия ветвей иногда также называют покрытием решений . Этот критерий требует, чтобы каждая точка входа и выхода в программе вызывалась хотя бы один раз, и каждое решение в программе принималось по всем возможным результатам хотя бы один раз. В этом контексте решение представляет собой логическое выражение , содержащее условия и ноль или более логических операторов. Это определение не то же самое, что покрытие ветвей, [6] однако термин « покрытие решений» иногда используется как его синоним. [7]
Покрытие условий/решений требует, чтобы были удовлетворены как покрытие решений, так и условия. Однако для приложений , критически важных для безопасности (таких как программное обеспечение авионики ), часто требуется, чтобы удовлетворялось покрытие модифицированных условий/решений (MC/DC) . Этот критерий расширяет критерии условия/решения требованиями, согласно которым каждое условие должно влиять на результат решения независимо.
Например, рассмотрим следующий код:
если ( a или b ) и c , то
Критерии условия/решения будут удовлетворяться следующим набором тестов:
Однако приведенный выше набор тестов не будет удовлетворять модифицированному покрытию условий/решений, поскольку в первом тесте значение «b», а во втором тесте значение «c» не будут влиять на выходные данные. Итак, для удовлетворения требований MC/DC необходим следующий набор тестов:
Этот критерий требует, чтобы проверялись все комбинации условий внутри каждого решения. Например, фрагмент кода из предыдущего раздела потребует восемь тестов:
Покрытие значений параметров (PVC) требует, чтобы в методе, принимающем параметры, учитывались все общие значения таких параметров. Идея состоит в том, что проверяются все возможные значения параметра. [8] Например, общие значения строки: 1) null , 2) пусто, 3) пробелы (пробел, табуляция, новая строка), 4) допустимая строка, 5) недопустимая строка, 6) однобайтовая строка, 7 ) двухбайтовая строка. Также может быть целесообразно использовать очень длинные строки. Невозможность проверить каждое возможное значение параметра может привести к ошибке. Тестирование только одного из них может привести к 100% покрытию кода, поскольку покрыта каждая строка, но поскольку тестируется только один из семи вариантов, PVC составляет только 14,2%.
Существуют и другие критерии покрытия, которые используются реже:
Критически важные для безопасности или надежные приложения часто должны продемонстрировать 100% того или иного вида тестового покрытия. Например, стандарт ECSS -E-ST-40C требует 100% покрытия операторов и решений для двух из четырех различных уровней критичности; для других целевые значения покрытия являются предметом переговоров между поставщиком и потребителем. [11] Однако установление конкретных целевых значений – и, в частности, 100% – подвергается критике со стороны практиков по разным причинам (см. [12] ). Мартин Фаулер пишет: «Я бы с подозрением отнесся к чему-то вроде 100% – это было бы запах того, что кто-то пишет тесты, чтобы порадовать показатели покрытия, но не думает о том, что он делает». [13]
Некоторые из приведенных выше критериев покрытия связаны между собой. Например, покрытие пути подразумевает покрытие решений, утверждений и входа/выхода. Покрытие решений подразумевает покрытие операторов, поскольку каждый оператор является частью ветви.
Полное покрытие трассы описанного выше типа обычно непрактично или невозможно. Любой модуль с последовательностью решений может иметь до путей внутри него; Конструкции цикла могут привести к бесконечному количеству путей. Многие пути также могут быть невозможными, поскольку в тестируемую программу не поступает никаких входных данных, которые могли бы вызвать выполнение этого конкретного пути. Однако было доказано, что универсальный алгоритм определения невозможных путей невозможен (такой алгоритм можно использовать для решения проблемы остановки ). [14] Тестирование базового пути — это, например, метод достижения полного покрытия ветвей без достижения полного покрытия пути. [15]
Вместо этого методы практического тестирования покрытия путей пытаются идентифицировать классы путей кода, которые различаются только количеством выполнения циклов, и для достижения покрытия «базового пути» тестер должен охватить все классы путей. [ нужна ссылка ] [ нужны разъяснения ]
Целевое программное обеспечение создано со специальными опциями или библиотеками и запускается в контролируемой среде, чтобы сопоставить каждую выполняемую функцию с функциональными точками в исходном коде. Это позволяет тестировать части целевого программного обеспечения, к которым редко или никогда не обращаются в нормальных условиях, и помогает убедиться в том, что наиболее важные условия (функциональные точки) были протестированы. Полученные выходные данные затем анализируются, чтобы увидеть, какие области кода не были проверены, и тесты обновляются, чтобы включать эти области по мере необходимости. В сочетании с другими методами тестового покрытия цель состоит в том, чтобы разработать строгий, но управляемый набор регрессионных тестов.
При реализации политик тестового покрытия в среде разработки программного обеспечения необходимо учитывать следующее:
Авторы программного обеспечения могут просмотреть результаты тестового покрытия, чтобы разработать дополнительные тесты и наборы входных данных или конфигураций для увеличения охвата жизненно важных функций. Двумя распространенными формами тестового покрытия являются покрытие операторов (или строк) и покрытие ветвей (или ребер). Покрытие строк сообщает о объеме выполнения тестирования с точки зрения того, какие строки кода были выполнены для завершения теста. Покрытие Edge сообщает, какие ветки или точки принятия решения по коду были выполнены для завершения теста. Оба они сообщают о показателе охвата, измеряемом в процентах. Значение этого зависит от того, какая форма(ы) покрытия использовалась, поскольку 67%-ное покрытие филиалов является более полным, чем 67%-ное покрытие выписок.
Как правило, инструменты тестового покрытия требуют вычислений и журналирования в дополнение к реальной программе, тем самым замедляя работу приложения, поэтому обычно этот анализ не выполняется в рабочей среде. Как и следовало ожидать, существуют классы программного обеспечения, которые невозможно подвергнуть таким тестам покрытия, хотя степень отображения покрытия можно приблизительно определить посредством анализа, а не прямого тестирования.
Существуют также некоторые виды дефектов, на которые влияют такие инструменты. В частности, некоторые состояния гонки или аналогичные операции, чувствительные к реальному времени , могут быть замаскированы при запуске в тестовых средах; хотя и наоборот, некоторые из этих дефектов может оказаться легче обнаружить в результате дополнительных затрат на код тестирования.
Большинство профессиональных разработчиков программного обеспечения используют покрытие C1 и C2. C1 означает покрытие операторов, а C2 — покрытие ветвей или условий. Комбинация C1 и C2 позволяет охватить большинство операторов в базе кода. Покрытие операторов также будет охватывать покрытие функций с входом и выходом, циклом, путем, потоком состояний, потоком управления и потоком данных. С помощью этих методов можно достичь почти 100% покрытия кода в большинстве программных проектов. [17]
Покрытие испытаний является одним из соображений при сертификации безопасности авиационного оборудования. Руководящие принципы, по которым авиационное оборудование сертифицируется Федеральным управлением гражданской авиации (FAA), задокументированы в документах DO-178B [16] и DO-178C . [18]
Покрытие испытаний также является требованием части 6 стандарта автомобильной безопасности ISO 26262 «Дорожные транспортные средства — функциональная безопасность» . [19]