Покрытие программного кода

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

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

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

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

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

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

Уровни покрытия

По строкам программного кода.

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

Вход: condition = true; Ожидаемый выход: *p = 123.

int* p = NULL;

If (condition)

p = &variable;

*p = 123;

Даже если в состав тестов не будет входить тестовый пример, проверяющий работу фрагмента при значении condition = false, код будет покрыт. Однако, в случае condition = false выполнение фрагмента вызовет ошибку.

Аналогичные проблемы возникают при проверке циклов do … while – при данном уровне покрытия достаточно выполнение цикла только один раз.

Другой особенностью данного метода является зависимость уровня покрытия от структуры программного кода. На практике часто не требуется 100% покрытия программного кода, вместо этого устанавливается допустимый уровень покрытия, например 75%. Проблемы могут возникнуть при покрытии следующего фрагмента программного кода:

If (condition)

functionA();

Else

functionB();

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

По ветвям условных операторов.

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

Также данный метод называют: branch coverage, all-edges coverage, basis path coverage, DC, C2, decision-decision-path.

В отличие от предыдущего уровня покрытия данный метод учитывает покрытие условных операторов с пустыми ветками. Так, для покрытия по веткам участка программного кода

a = 0;

If (condition)

{

a = 1;

}

необходимы два тестовых примера:

1. Вход: condition = true; Ожидаемый выход: a = 1;

2. Вход: condition = false; Ожидаемый выход: a = 0;

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

if ( condition1 && ( condition2 || function1() ) )

statement1;

Else

statement2;

полное покрытие по веткам может быть достигнуто при помощи двух тестовых примеров:

1. Вход: condition1 = true, condition2 = true

2. Вход: condition1 = false, condition2 = true/false (любое значение)

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

3. Вход: condition1 = true, condition2 = false.

Наши рекомендации