Критерии тестирования программ

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

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

Одним из методов структурного тестирования программ является анализ покрытия. Анализ покрытия служит решению следующих задач:

· нахождение неисполняемых участков программы (после выполнения набора тестов);

· создание дополнительных тестов для увеличения степени покрытия;

· выявление избыточных тестов (тестов, которые не увеличивают степень покрытия);

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

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

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

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

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

4.5.5.1. Покрытие операторов

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

Основной недостаток критерия C0 состоит в том, что он нечувствителен к некоторым управляющим структурам. Рассмотрим, например следующий фрагмент C/C++ кода:

int* p = NULL;

if (b)

p = &nInt;

*p = 0xFFFFFFFF;

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

4.5.5.2. Покрытие решений

Критерий покрытия решений требует, чтобы каждое двоичное выражение в управляющих структурах (таких как операторы if и while) приняло как значение ИСТИНА, так и значение ЛОЖЬ. Двоичное выражение рассматривается как одно целое (истинный или ложный предикат), структура двоичного выражения не учитывается. Отметим, что к управляющим структурам относятся и все операторы, выполнение которых может привести к возникновению исключения или вызову обработчика прерывания.

Этот критерий также называют критерием покрытия ветвей, покрытия дуг, покрытия основных путей, критерием C1.

К преимуществам этого критерия можно отнести его простоту и отсутствие недостатков, присущих критерию C0. Слабой стороной данного критерия является то, что при нем игнорируются некоторые ветви из-за сокращенной обработки логических выражений. Рассмотрим, например, следующий фрагмент C/C++/Java кода:

if (b && (b2 || function()))

statement1;

else

statement2;

Если условия b1 и b2 примут значения а) true, true и б) false, true соответственно, то логическое условия в операторе if примет как истинное, так и ложное значение. Таким образом, данный фрагмент будет полностью протестирован по критерию C2, хотя функция function()так ни разу и не вызывалась.

4.5.5.3. Покрытие условий и решений/условий

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

Усилением критерия покрытия условий является критерий критерий множественного покрытия условий который требует, чтобы логические подвыражения в условиях приняли все возможные сочетания значений ИСТИНА и ЛОЖЬ.

Объединение требований критериев покрытия решений и условий привело к появлению критерия покрытия решений/условий.

4.5.5.4. Покрытие путей

Критерий покрытия путей исследует выполнение функции по всем возможным путям (последовательностям ветвей) от точки входа до точки выхода.

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

Тестирование путей приводит к очень тщательному тестированию программы. Критерий тестирования путей имеет два серьезных недостатка:

· число путей экспоненциально зависит от числа ветвей;

· многие пути являются нереализуемыми.

Рассмотрим следующий фрагмент C/C++ кода:

if (b)

statement1;

statement2;

if (b)

statement3;

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

4.5.5.5. Покрытие функций

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

4.5.5.6. Покрытие вызовов функций

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

4.5.5.7. Критерии, основанные на анализе потока данных

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

4.5.5.8. Сравнение критериев покрытия

Говорят, что критерий K1 сильнее критерия K2, или критерий K1 включает критерий K2, если тестирование программы по критерию K1 приводит к тестированию программы по критерию K2. Если критерий K1 не включает критерий K2, и критерий K2 не включает критерий K1, то критерии K1 и K2 не сравнимы. Для некоторых описанных выше критериев можно получить отношения их взаимной сравнимости:

Покрытие решений включает покрытие операторов (так как выполнение всех ветвей в программе приводит к выполнению всех операторов).

Покрытие решений/условий включает покрытие решений и покрытие условий (по определению).

Покрытие путей включает покрытие решений.