Тестовое окружение, рассмотренное в предыдущей лекции, обеспечивает процесс тестирования необходимой инфраструктурой и поддерживает ее. Непосредственно для тестирования кроме тестового окружения необходимо определить проверочные задачи, которые будет выполнять система или ее часть. Такие проверочные задачи называют тестовыми примерами.
Как уже было сказано выше, каждый тестовый пример состоит из входных значений для системы, описания сценария работы примера и ожидаемых выходных значений. Цель выполнения любого тестового примера - либо продемонстрировать наличие в системе дефекта, либо доказать его отсутствие.
Основным источником информации для создания тестовых примеров является различного рода документация на систему, например, функциональные требования и требования к интерфейсу.
Функциональные требования описывают поведение системы как "черного ящика", т.е. исключительно с позиций того, что должна делать система в различных ситуациях. Иными словами, функциональные требования определяют реакцию системы на различные входные воздействия.
Например, функциональные требования на программный модуль, рассчитывающий и проверяющий контрольную сумму для записи, могут выглядеть следующим образом.
Функциональные требования на модуль расчета и проверки контрольной суммы
Внешний интерфейс модуля
1. Структура record_type
struct record_type
{
bool A;
int B[20];
signed char C[5];
unsigned int CRC;
double D[1];
}
2. Переменная Empty
bool Empty;
3. Функция подсчета контрольной суммы записи Set_CRC
void Set_CRC(record_type record);
Вход:
Запись record, c неопределенным значением поля CRC.
Выход:
Запись record, с вычисленным по заданным правилам значение поля CRC.
Переменная Empty.
4. Функция проверки контрольной суммы записи Сheck_CRC
bool Check_CRC(record_type record);
Вход:
Запись Rec_Mess c определенным значением поля CRC.
Выход:
Возвращаемое значение true или false. Переменная Empty.
Функциональные требования
1. Инициализация модуля
При инициализации модуля переменная Empty должна быть установлена в значение TRUE.
2. Подсчет контрольной суммы записи
a. Расчет контрольной суммы
Процедура Set_CRC должна производить подсчет контрольной суммы записи Rec_Mess по алгоритму CRC32.
При подсчете контрольной суммы значение поля CRC не должно участвовать в суммировании. На основании
произведенных расчетов должно быть вычислено и определено значение поля CRC таким образом, чтобы при
подсчете контрольной суммы вместе с установленным значением этого поля контрольная сумма равнялась нулю.
b. Установка значения переменной Empty
Если все байты полей записи (кроме возможно CRC поля) имеют нулевое значение (код 00000000B),
то значение переменной Empty должно быть установлено в TRUE.
Ели хотя бы один байт записи (исключая байты поля CRC) не нулевой, то значение переменной
Empty должно быть установлено в FALSE.
3. Проверка контрольной суммы записи
a. Проверка контрольной суммы
Процедура должна вычислять по заданному алгоритму CRC32 контрольную сумму записи Rec_Mess.
Возвращаемое процедурой значение должно быть равно TRUE, если подсчитанное значение равно нулю.
При ненулевом значении подсчитанной контрольной суммы должно возвращаться значение FALSE.
b. Установка значения переменной Empty
Если все байты полей записи, включая значение CRC поля, имеют нулевое значение (код 00000000B),
то значение переменной Empty должно быть установлено в TRUE.
Ели хотя бы один байт записи не нулевой, то значение переменной Empty должно быть установлено в FALSE.
Начальный этап работы тестировщика заключается в формировании тест-требований, соответствующих функциональным требованиям. Основная цель тест-требований - определить, какая функциональность системы должна быть протестирована. В самом простом случае одному функциональному требованию соответствует одно тест-требование. Однако чаще всего тест-требования детализируют формулировки функциональных требований.
Тест-требования определяют, что должно быть протестировано, но не определяют, как это должно быть сделано. Например, для перечисленных выше функциональных требований можно сформулировать следующие тест-требования.
Тест-требования
1. Проверка инициализация модуля
Проверить, что начальное значение переменной Empty установлено TRUE.
2. Проверка подсчета контрольной суммы
a. Проверить, что в процедуре Set_CRC вычисление контрольной суммы производится по правилам алгоритма CRC32,
как определено в секции 2a функциональных требований.
b. Проверить, что вычисленное значение контрольной суммы не зависит от начального значения поля CRC.
c. Проверить, что вычисленное значение контрольной суммы не зависит от значений байт выравнивания полей записи.
d. Проверить, что значение переменной Empty устанавливается при каждом вызове функции Set_CRC в зависимости от значений
полей записи, как определено в секции 2b функциональных требований.
3. Проверка процедуры Check_CRC
a. Проверить, что при обращении к процедуре Check_CRC вычисление контрольной суммы производится по правилам
алгоритма CRC32, как определено в секции 3a функциональных требований.
b. Проверить, что возвращаемое значение равно TRUE, если контрольная сумма проверяемой записи правильная,
и FALSE - в противном случае.
c. Проверить, что проверка правильности значения контрольной суммы не зависит от значений байт выравнивания
полей записи.
d. Проверить, что значение переменной Empty устанавливается при каждом вызове функции Check_CRC в зависимости
от значений полей записи, как определено в секции 3b функциональных требований.
Особенности реализации тестового окружения и конкретные значения, подаваемые на вход системы и ожидаемые на ее выходе, определяются тестовыми примерами. Одному тест-требованию соответствует как минимум один тестовый пример.
Рассмотрим различные классы тестовых примеров, направленные на выявление различных дефектов в работе программной системы.
Чаще всего дефекты в программных системах проявляются при обработке нестандартных данных, не предусмотренных требованиями - при вводе неверных символов, пустых строк или при слишком большой скорости ввода информации. Однако, перед поиском таких дефектов необходимо удостовериться в том, что программа корректно обрабатывает верные данные, предусмотренные спецификацией, т.е. проверить работу основных алгоритмов. Так, для функции вычисления контрольной суммы допустимыми входными данными будет произвольная запись, содержащая данные во всех полях, кроме поля контрольной суммы CRC.
record_type test_value1;
int i;
test_value1.A = false;
for (i=0;i<20;i++)
test_value1.B[i] = i;
for (i=0;i<5;i++)
test_value1.C[i] = i+5;
test_value1.D[0] = i+8;
test_value1.CRC = 0;
Set_CRC(test_value1);
printf("%d\n", test_value1.CRC);
Сценарием будет вызов функции Set_CRC, а ожидаемым выходным значением - корректное значение поля CRC, рассчитанное по алгоритму CRC32.
Обычно для проверки допустимых данных достаточно одного тестового примера. Но функциональные требования могут определять различные группы допустимых данных, которые могут объединяться в классы эквивалентности. В этом случае необходимо определять как минимум один тестовый пример для одного класса эквивалентности. Более подробно речь о классах эквивалентности пойдет далее.
Отдельный вид допустимых данных, передача которых в систему может вскрыть дефект - граничные данные, т.е. например, числа, значения которых являются предельными для их типа, строки предельной или нулевой длины и т.п. Обычно при помощи тестирования граничных условий выявляются проблемы с арифметическим сравнением чисел или с итераторами циклов.
Для тестирования функции Set_CRC на граничных условиях можно определить два тестовых примера с минимальными и максимальными значениями полей в записи.
record_type test_value2;
record_type test_value3;
int i;
test_value2.A = false;
for (i=0;i<20;i++)
test_value2.B[i] = 0;
for (i=0;i<5;i++)
test_value2.C[i] = 0;
test_value2.D[0] = 0;
test_value2.CRC = 0;
Set_CRC(test_value2);
printf("%d\n", test_value2.CRC);
test_value3.A = true;
for (i=0;i<20;i++)
test_value3.B[i] = pow(2,sizeof(test_value3.B[i])*8)-1;
for (i=0;i<5;i++)
test_value3.C[i] = pow(2,sizeof(test_value3.C[i])*8)-1;
test_value3.D[0] = pow(2,sizeof(test_value3.D[0])*8)-1;
test_value3.CRC = pow(2,sizeof(test_value3.CRC)*8)-1;
Set_CRC(test_value3);
printf("%d\n", test_value3.CRC);
Дефекты могут проявиться и в случае, если системе не передается никаких данных или передаются данные нулевого размера. Для тестирования функции Set_CRC при отсутствии данных можно вызвать ее, передав в качестве параметра неинициализированную структуру. Однако такой тест не является точным примером отсутствия данных, скорее, это пример случайных данных (возможно - неверных).
record_type test_value4;
Set_CRC(test_value4);
printf("%d\n", test_value4.CRC);
В случае повторной передачи на вход системы тех же самых данных могут получаться различия в выходных данных, не предусмотренные в требованиях. Как правило, дефекты такого типа проявляются в результате того, что система не устанавливает внутренние переменные в исходное состояние или в результате ошибок округления.
record_type test_value5;
int i;
test_value5.A = false;
for (i=0;i<20;i++)
test_value5.B[i] = i;
for (i=0;i<5;i++)
test_value5.C[i] = i+5;
test_value5.D[0] = i+8;
test_value5.CRC = 0;
Set_CRC(test_value5);
printf("%d\n", test_value5.CRC);
Set_CRC(test_value5);
printf("%d\n", test_value5.CRC);
При проверке поведения системы необходимо не забывать проверять ее поведение при передаче ей данных, не предусмотренных требованиями - слишком длинных или слишком коротких строк, неверных символов, чисел за пределами вычислимого диапазона и т.п. Неверные данные, как и допустимые, также можно разделять на различные классы эквивалентности. Примером неверных данных для функции Set_CRC может служить запись с другой структурой, переданная в функцию через приведение типов. Если расчет контрольной суммы использует имена полей записи, то контрольная сумма может оказаться вычисленной неверно или может произойти перезапись областей памяти, не предназначенных для хранения данных.
struct record_type2
{
int F;
int G[45];
int H[8];
unsigned int CRC;
int K[2];
}
record_type2 test_value6;
Set_CRC((record_type)test_value6);
printf("%d\n", test_value6.CRC);
Механизмы повторной инициализации системы во время ее работы также могут содержать дефекты. В первую очередь, эти дефекты могут проявляться в том, что не все внутренние данные системы после реинициализации придут в начальное состояние. В результате может произойти сбой в работе системы.
Примером реинициализации модуля вычисления CRC может служить принудительное обнуление переменной empty.
record_type test_value7;
int i;
test_value7.A = false;
for (i=0;i<20;i++)
test_value7.B[i] = i;
for (i=0;i<5;i++)
test_value7.C[i] = i+5;
test_value7.D[0] = i+8;
test_value7.CRC = 0;
Set_CRC(test_value7);
printf("%d\n", test_value1.CRC);
empty=true;
Set_CRC(test_value7);
printf("%d\n", test_value1.CRC);
Под устойчивостью системы можно понимать ее способность выдерживать нештатную нагрузку, явно не предусмотренную требованиями. Например, стоит вопрос, сохранит ли система работоспособность после 10 тысяч вызовов. Для функции Set_CRC можно реализовать следующий тестовый пример:
record_type test_value8;
int i;
test_value8.A = false;
for (i=0;i<20;i++)
test_value8.B[i] = i;
for (i=0;i<5;i++)
test_value8.C[i] = i+5;
test_value8.D[0] = i+8;
test_value8.CRC = 0;
for (i=0;i<10000;i++)
Set_CRC(test_value8);
printf("%d\n", test_value1.CRC);
Аналогичный анализ может быть сделан путем просмотра текста программы (если он доступен при тестировании) на основании отсутствия "истории" (хранимых данных) в реализации программы, т.е. данных, значение которых может меняться в зависимости от количества запусков программы. Таким образом, в ряде случаев тестирование может быть заменено анализом программного кода.
Нештатные состояния среды выполнения (например, исчерпание памяти, дискового пространства или длительная нехватка процессорного времени) могут затруднять работу системы либо делать ее невозможной. Основная задача системы в такой ситуации - корректно завершить или приостановить свою работу.
Примером тестового примера, создающего нештатное состояние среды, для функции Set_CRC может служить выделение всей свободной памяти перед вызовом функции. Если Set_CRC использует динамическую память, то в ней должны присутствовать проверки на возможность выделить память, в противном случае выполнение функции вызовет ее аварийное завершение:
record_type test_value9;
int i;
int *heap;
heap = malloc(_MAXMEM);
test_value9.A = false;
for (i=0;i<20;i++)
test_value9.B[i] = i;
for (i=0;i<5;i++)
test_value9.C[i] = i+5;
test_value9.D[0] = i+8;
test_value9.CRC = 0;
Set_CRC(test_value9);
free(heap);
printf("%d\n", test_value9.CRC);
В тестовых примерах, прямо соответствующих тест-требованиям, обычно используются входные значения, находящиеся заведомо внутри допустимого диапазона. Один из способов проверки устойчивости системы на значениях, близких к предельным, - создавать для каждого входа как минимум три тестовых примера:
Для еще большей уверенности в работоспособности системы используют пять тестовых примеров:
Такой способ проверки называется проверкой на граничных значениях. Такая проверка позволяет выявлять проблемы, связанные с выходом за границы диапазона. Например, если в функцию
char sum(char a, char b)
{
return a+b;
}
вычисляющую сумму чисел a и b, будут переданы значения 255 и 255, то в случае отсутствия специальной обработки ситуации переполнения сумма будет вычислена неверно.
Другая область, при тестировании которой полезно пользоваться проверкой на граничных значениях, - индексы массивов. Например, функция
void abs_array(char array[], char size)
{
for (int i=1;i<=size;i++)
{
array[i] = abs(array[i]);
}
return;
}
заменяющая значение на значение по модулю у каждого элемента переданного ей массива, содержит ошибку в цикле for, которая может быть легко обнаружена при передаче в функцию массива единичного размера.
Робастность системы - это степень ее чувствительности к факторам, не учтенным на этапах ее проектирования, например, к неточности основного алгоритма, приводящего к ошибкам округления при вычислениях, сбоям во внешней среде или к данным, значения которых находятся вне допустимого диапазона. Чаще всего под робастностью программных систем понимают именно устойчивость к некорректным данным. Система должна быть способна корректно обрабатывать такие данные путем выдачи соответствующих сообщений об ошибках, сбои и отказы системы на подобных данных недопустимы.
Для тестирования робастности к тестовым примерам, рассмотренным в предыдущем разделе, добавляются еще два тестовых примера:
проверяющие поведение системы за границей допустимого диапазона, а также в случае тестирования операций сравнения дополнительно дающие гарантию того, что в них не допущена опечатка.
Таким образом, если изобразить допустимый интервал как на Рис 4.1, то можно видеть, что для тестирования интервальных значений достаточно 7 тестовых примеров - пяти допустимых и двух на робастность.
(рис 4.1) Рекомендуемые проверочные значенияВ литературе часто встречается утверждение, что значение внутри интервала является избыточным и его тестирование не требуется. Однако, проверка внутреннего значения является полезной как минимум с психологической точки зрения, а также в случае, если интервал ограничен сложными граничными условиями. Также рекомендуется отдельно проверять значение 0 (даже если оно находится внутри интервала), т.к. зачастую это значение обрабатывается некорректно (например, в случае деления на 0).
При разработке тестовых примеров может возникнуть такая ситуация, в которой различные входные значения приводят к одним и тем же реакциям системы. Если при этом такие входные значения имеют что-то общее, то возможно объединение таких значений в классы эквивалентности, т.е. выполнение эквивалентного разбиения множества допустимых входных значений.
Разбиение на классы эквивалентности - это, в первую очередь, способ уменьшения необходимого числа тестовых примеров. Обычно, если в тест-требованиях специально не оговорено иное, при тестировании достаточно выполнить только один тестовый пример для каждого класса эквивалентности. Разбиение на классы эквивалентности особенно полезно, когда на вход системы может быть подано большое количество различных значений; тестирование каждого возможного значения привело бы к слишком большому объему тестирования.
Рассмотренные выше граничные условия могут служить примером классов эквивалентности:
Таким образом, тестирование граничных условий и робастности является частным случаем тестирования с использованием классов эквивалентности - вместо того, чтобы тестировать все недопустимые значения, выбираются только соседние с граничными.
При определении классов эквивалентности следует руководствоваться следующими правилами:
Другим примером разбиения на классы эквивалентности может служить тестирование открытия файла по его имени. В результате тестирования необходимо определить, все ли варианты имен обрабатываются системой согласно следующим тест-требованиям.
Входными значениями тестового примера являются различные имена файлов, выходными - реакция системы (ошибка или успешное открытие).
Можно выделить следующие классы эквивалентности:
По длине имени:
По символам:
Эти классы эквивалентности иллюстрируют, что проверки на границах интервалов применимы не только для тестирования арифметических операций и операций сравнения. Практически для любых данных, даже текстовых, можно определить "минимальные" и "максимальные" допустимые значения.
Разбиение на классы эквивалентности широко используется при тестировании корректности реализации арифметических операций и операций сравнения. Каждую операцию можно рассматривать как блок с входами - значениям и выходом - результатом операции. Для ее тестирования выполняется разбиение диапазона изменения переменных на входах блока на классы эквивалентности и методом анализа граничных значений этих переменных.
В таблице 4.1 приведены тестовые наборы для блоков, реализующих операции сравнения, в случае, когда на один из входов блока подается константа.
| greaterThan блок. Реализует операцию сравнения | greaterEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Вход a | b - d | b + d | b | min | max | Вход a | b - d | b + d | b | min | max |
| Выход | F | T | F | F | T | Выход | F | T | T | F | T |
| lessThan блок. Реализует операцию сравнения | lessEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
| Вход a | b - d | b + d | b | min | max | Вход a | b - d | b + d | b | min | max |
| Выход | T | F | F | T | F | Выход | T | F | T | T | F |
| equalTo блок. Реализует операцию сравнения | notEqualTo блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3 | 4 | № набора | 1 | 2 | 3 | 4 | ||
| Вход a | $$\ne$$ b | b | min | max | Вход a | $$\ne$$ b | b | min | max | ||
| Выход | F | T | F | F | Выход | T | F | T | T | ||
* тестовый набор реализуем, только если переменная на входе a - переменная целого типа
В приведенных тестовых наборах используются следующие обозначения:
d - шаг изменения ( resolution ) переменной на входе a. Если переменная на входе a - переменная целого типа, то d равно 1;min - минимальное значение переменной на входе a ;max - максимальное значение переменной на входе a.В таблице 4.2 приведены тестовые наборы для блоков, реализующих операции сравнения, в случае, когда на оба входа блока подаются переменные.
| greaterThan блок. Реализует операцию сравнения | greaterEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Вход a | val | val | val | min | max | Вход a | val | val | val | min | max |
| Вход b | val + d2 | val - d2 | val | max | min | Вход b | val + d2 | val - d2 | val | max | min |
| Выход | F | T | F | F | T | Выход | F | T | T | F | T |
| lessThan блок. Реализует операцию сравнения | lessEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
| Вход a | val | val | val | min | max | Вход a | val | val | val | min | max |
| Вход b | val + d2 | val - d2 | val | max | min | Вход b | val + d2 | val - d2 | val | max | min |
| Выход | T | F | F | T | F | Выход | T | F | T | T | F |
| equalTo блок. Реализует операцию сравнения | notEqualTo блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3 | 4 | № набора | 1 | 2 | 3 | 4 | ||
| Вход a | val1 | val | min | max | Вход a | val1 | val | min | max | ||
| Вход b | val2 | val | max | min | Вход b | val2 | val | max | min | ||
| Выход | F | T | F | F | Выход | T | F | T | T | ||
* тестовый набор реализуем, только если переменные на входах блока - переменные целого типа
В приведенных тестовых наборах используются следующие обозначения:
d2 - шаг изменения ( resolution ) переменной на входе b. Если переменная на входе b - переменная целого типа, то d2 равно 1;val, val1, val2 - значения, которые взяты из середины диапазона, полученного при пересечении диапазонов переменных на входах a и b ;min - минимальное значение переменной на входе блока;max - максимальное значение переменной на входе блока.Тестовое окружение, рассмотренное в предыдущей лекции, обеспечивает процесс тестирования необходимой инфраструктурой и поддерживает ее. Непосредственно для тестирования кроме тестового окружения необходимо определить проверочные задачи, которые будет выполнять система или ее часть. Такие проверочные задачи называют тестовыми примерами.
Как уже было сказано выше, каждый тестовый пример состоит из входных значений для системы, описания сценария работы примера и ожидаемых выходных значений. Цель выполнения любого тестового примера - либо продемонстрировать наличие в системе дефекта, либо доказать его отсутствие.
Основным источником информации для создания тестовых примеров является различного рода документация на систему, например, функциональные требования и требования к интерфейсу.
Функциональные требования описывают поведение системы как "черного ящика", т.е. исключительно с позиций того, что должна делать система в различных ситуациях. Иными словами, функциональные требования определяют реакцию системы на различные входные воздействия.
Например, функциональные требования на программный модуль, рассчитывающий и проверяющий контрольную сумму для записи, могут выглядеть следующим образом.
Функциональные требования на модуль расчета и проверки контрольной суммы
Внешний интерфейс модуля
1. Структура record_type
struct record_type
{
bool A;
int B[20];
signed char C[5];
unsigned int CRC;
double D[1];
}
2. Переменная Empty
bool Empty;
3. Функция подсчета контрольной суммы записи Set_CRC
void Set_CRC(record_type record);
Вход:
Запись record, c неопределенным значением поля CRC.
Выход:
Запись record, с вычисленным по заданным правилам значение поля CRC.
Переменная Empty.
4. Функция проверки контрольной суммы записи Сheck_CRC
bool Check_CRC(record_type record);
Вход:
Запись Rec_Mess c определенным значением поля CRC.
Выход:
Возвращаемое значение true или false. Переменная Empty.
Функциональные требования
1. Инициализация модуля
При инициализации модуля переменная Empty должна быть установлена в значение TRUE.
2. Подсчет контрольной суммы записи
a. Расчет контрольной суммы
Процедура Set_CRC должна производить подсчет контрольной суммы записи Rec_Mess по алгоритму CRC32.
При подсчете контрольной суммы значение поля CRC не должно участвовать в суммировании. На основании
произведенных расчетов должно быть вычислено и определено значение поля CRC таким образом, чтобы при
подсчете контрольной суммы вместе с установленным значением этого поля контрольная сумма равнялась нулю.
b. Установка значения переменной Empty
Если все байты полей записи (кроме возможно CRC поля) имеют нулевое значение (код 00000000B),
то значение переменной Empty должно быть установлено в TRUE.
Ели хотя бы один байт записи (исключая байты поля CRC) не нулевой, то значение переменной
Empty должно быть установлено в FALSE.
3. Проверка контрольной суммы записи
a. Проверка контрольной суммы
Процедура должна вычислять по заданному алгоритму CRC32 контрольную сумму записи Rec_Mess.
Возвращаемое процедурой значение должно быть равно TRUE, если подсчитанное значение равно нулю.
При ненулевом значении подсчитанной контрольной суммы должно возвращаться значение FALSE.
b. Установка значения переменной Empty
Если все байты полей записи, включая значение CRC поля, имеют нулевое значение (код 00000000B),
то значение переменной Empty должно быть установлено в TRUE.
Ели хотя бы один байт записи не нулевой, то значение переменной Empty должно быть установлено в FALSE.
Начальный этап работы тестировщика заключается в формировании тест-требований, соответствующих функциональным требованиям. Основная цель тест-требований - определить, какая функциональность системы должна быть протестирована. В самом простом случае одному функциональному требованию соответствует одно тест-требование. Однако чаще всего тест-требования детализируют формулировки функциональных требований.
Тест-требования определяют, что должно быть протестировано, но не определяют, как это должно быть сделано. Например, для перечисленных выше функциональных требований можно сформулировать следующие тест-требования.
Тест-требования
1. Проверка инициализация модуля
Проверить, что начальное значение переменной Empty установлено TRUE.
2. Проверка подсчета контрольной суммы
a. Проверить, что в процедуре Set_CRC вычисление контрольной суммы производится по правилам алгоритма CRC32,
как определено в секции 2a функциональных требований.
b. Проверить, что вычисленное значение контрольной суммы не зависит от начального значения поля CRC.
c. Проверить, что вычисленное значение контрольной суммы не зависит от значений байт выравнивания полей записи.
d. Проверить, что значение переменной Empty устанавливается при каждом вызове функции Set_CRC в зависимости от значений
полей записи, как определено в секции 2b функциональных требований.
3. Проверка процедуры Check_CRC
a. Проверить, что при обращении к процедуре Check_CRC вычисление контрольной суммы производится по правилам
алгоритма CRC32, как определено в секции 3a функциональных требований.
b. Проверить, что возвращаемое значение равно TRUE, если контрольная сумма проверяемой записи правильная,
и FALSE - в противном случае.
c. Проверить, что проверка правильности значения контрольной суммы не зависит от значений байт выравнивания
полей записи.
d. Проверить, что значение переменной Empty устанавливается при каждом вызове функции Check_CRC в зависимости
от значений полей записи, как определено в секции 3b функциональных требований.
Особенности реализации тестового окружения и конкретные значения, подаваемые на вход системы и ожидаемые на ее выходе, определяются тестовыми примерами. Одному тест-требованию соответствует как минимум один тестовый пример.
Рассмотрим различные классы тестовых примеров, направленные на выявление различных дефектов в работе программной системы.
Чаще всего дефекты в программных системах проявляются при обработке нестандартных данных, не предусмотренных требованиями - при вводе неверных символов, пустых строк или при слишком большой скорости ввода информации. Однако, перед поиском таких дефектов необходимо удостовериться в том, что программа корректно обрабатывает верные данные, предусмотренные спецификацией, т.е. проверить работу основных алгоритмов. Так, для функции вычисления контрольной суммы допустимыми входными данными будет произвольная запись, содержащая данные во всех полях, кроме поля контрольной суммы CRC.
record_type test_value1;
int i;
test_value1.A = false;
for (i=0;i<20;i++)
test_value1.B[i] = i;
for (i=0;i<5;i++)
test_value1.C[i] = i+5;
test_value1.D[0] = i+8;
test_value1.CRC = 0;
Set_CRC(test_value1);
printf("%d\n", test_value1.CRC);
Сценарием будет вызов функции Set_CRC, а ожидаемым выходным значением - корректное значение поля CRC, рассчитанное по алгоритму CRC32.
Обычно для проверки допустимых данных достаточно одного тестового примера. Но функциональные требования могут определять различные группы допустимых данных, которые могут объединяться в классы эквивалентности. В этом случае необходимо определять как минимум один тестовый пример для одного класса эквивалентности. Более подробно речь о классах эквивалентности пойдет далее.
Отдельный вид допустимых данных, передача которых в систему может вскрыть дефект - граничные данные, т.е. например, числа, значения которых являются предельными для их типа, строки предельной или нулевой длины и т.п. Обычно при помощи тестирования граничных условий выявляются проблемы с арифметическим сравнением чисел или с итераторами циклов.
Для тестирования функции Set_CRC на граничных условиях можно определить два тестовых примера с минимальными и максимальными значениями полей в записи.
record_type test_value2;
record_type test_value3;
int i;
test_value2.A = false;
for (i=0;i<20;i++)
test_value2.B[i] = 0;
for (i=0;i<5;i++)
test_value2.C[i] = 0;
test_value2.D[0] = 0;
test_value2.CRC = 0;
Set_CRC(test_value2);
printf("%d\n", test_value2.CRC);
test_value3.A = true;
for (i=0;i<20;i++)
test_value3.B[i] = pow(2,sizeof(test_value3.B[i])*8)-1;
for (i=0;i<5;i++)
test_value3.C[i] = pow(2,sizeof(test_value3.C[i])*8)-1;
test_value3.D[0] = pow(2,sizeof(test_value3.D[0])*8)-1;
test_value3.CRC = pow(2,sizeof(test_value3.CRC)*8)-1;
Set_CRC(test_value3);
printf("%d\n", test_value3.CRC);
Дефекты могут проявиться и в случае, если системе не передается никаких данных или передаются данные нулевого размера. Для тестирования функции Set_CRC при отсутствии данных можно вызвать ее, передав в качестве параметра неинициализированную структуру. Однако такой тест не является точным примером отсутствия данных, скорее, это пример случайных данных (возможно - неверных).
record_type test_value4;
Set_CRC(test_value4);
printf("%d\n", test_value4.CRC);
В случае повторной передачи на вход системы тех же самых данных могут получаться различия в выходных данных, не предусмотренные в требованиях. Как правило, дефекты такого типа проявляются в результате того, что система не устанавливает внутренние переменные в исходное состояние или в результате ошибок округления.
record_type test_value5;
int i;
test_value5.A = false;
for (i=0;i<20;i++)
test_value5.B[i] = i;
for (i=0;i<5;i++)
test_value5.C[i] = i+5;
test_value5.D[0] = i+8;
test_value5.CRC = 0;
Set_CRC(test_value5);
printf("%d\n", test_value5.CRC);
Set_CRC(test_value5);
printf("%d\n", test_value5.CRC);
При проверке поведения системы необходимо не забывать проверять ее поведение при передаче ей данных, не предусмотренных требованиями - слишком длинных или слишком коротких строк, неверных символов, чисел за пределами вычислимого диапазона и т.п. Неверные данные, как и допустимые, также можно разделять на различные классы эквивалентности. Примером неверных данных для функции Set_CRC может служить запись с другой структурой, переданная в функцию через приведение типов. Если расчет контрольной суммы использует имена полей записи, то контрольная сумма может оказаться вычисленной неверно или может произойти перезапись областей памяти, не предназначенных для хранения данных.
struct record_type2
{
int F;
int G[45];
int H[8];
unsigned int CRC;
int K[2];
}
record_type2 test_value6;
Set_CRC((record_type)test_value6);
printf("%d\n", test_value6.CRC);
Механизмы повторной инициализации системы во время ее работы также могут содержать дефекты. В первую очередь, эти дефекты могут проявляться в том, что не все внутренние данные системы после реинициализации придут в начальное состояние. В результате может произойти сбой в работе системы.
Примером реинициализации модуля вычисления CRC может служить принудительное обнуление переменной empty.
record_type test_value7;
int i;
test_value7.A = false;
for (i=0;i<20;i++)
test_value7.B[i] = i;
for (i=0;i<5;i++)
test_value7.C[i] = i+5;
test_value7.D[0] = i+8;
test_value7.CRC = 0;
Set_CRC(test_value7);
printf("%d\n", test_value1.CRC);
empty=true;
Set_CRC(test_value7);
printf("%d\n", test_value1.CRC);
Под устойчивостью системы можно понимать ее способность выдерживать нештатную нагрузку, явно не предусмотренную требованиями. Например, стоит вопрос, сохранит ли система работоспособность после 10 тысяч вызовов. Для функции Set_CRC можно реализовать следующий тестовый пример:
record_type test_value8;
int i;
test_value8.A = false;
for (i=0;i<20;i++)
test_value8.B[i] = i;
for (i=0;i<5;i++)
test_value8.C[i] = i+5;
test_value8.D[0] = i+8;
test_value8.CRC = 0;
for (i=0;i<10000;i++)
Set_CRC(test_value8);
printf("%d\n", test_value1.CRC);
Аналогичный анализ может быть сделан путем просмотра текста программы (если он доступен при тестировании) на основании отсутствия "истории" (хранимых данных) в реализации программы, т.е. данных, значение которых может меняться в зависимости от количества запусков программы. Таким образом, в ряде случаев тестирование может быть заменено анализом программного кода.
Нештатные состояния среды выполнения (например, исчерпание памяти, дискового пространства или длительная нехватка процессорного времени) могут затруднять работу системы либо делать ее невозможной. Основная задача системы в такой ситуации - корректно завершить или приостановить свою работу.
Примером тестового примера, создающего нештатное состояние среды, для функции Set_CRC может служить выделение всей свободной памяти перед вызовом функции. Если Set_CRC использует динамическую память, то в ней должны присутствовать проверки на возможность выделить память, в противном случае выполнение функции вызовет ее аварийное завершение:
record_type test_value9;
int i;
int *heap;
heap = malloc(_MAXMEM);
test_value9.A = false;
for (i=0;i<20;i++)
test_value9.B[i] = i;
for (i=0;i<5;i++)
test_value9.C[i] = i+5;
test_value9.D[0] = i+8;
test_value9.CRC = 0;
Set_CRC(test_value9);
free(heap);
printf("%d\n", test_value9.CRC);
В тестовых примерах, прямо соответствующих тест-требованиям, обычно используются входные значения, находящиеся заведомо внутри допустимого диапазона. Один из способов проверки устойчивости системы на значениях, близких к предельным, - создавать для каждого входа как минимум три тестовых примера:
Для еще большей уверенности в работоспособности системы используют пять тестовых примеров:
Такой способ проверки называется проверкой на граничных значениях. Такая проверка позволяет выявлять проблемы, связанные с выходом за границы диапазона. Например, если в функцию
char sum(char a, char b)
{
return a+b;
}
вычисляющую сумму чисел a и b, будут переданы значения 255 и 255, то в случае отсутствия специальной обработки ситуации переполнения сумма будет вычислена неверно.
Другая область, при тестировании которой полезно пользоваться проверкой на граничных значениях, - индексы массивов. Например, функция
void abs_array(char array[], char size)
{
for (int i=1;i<=size;i++)
{
array[i] = abs(array[i]);
}
return;
}
заменяющая значение на значение по модулю у каждого элемента переданного ей массива, содержит ошибку в цикле for, которая может быть легко обнаружена при передаче в функцию массива единичного размера.
Робастность системы - это степень ее чувствительности к факторам, не учтенным на этапах ее проектирования, например, к неточности основного алгоритма, приводящего к ошибкам округления при вычислениях, сбоям во внешней среде или к данным, значения которых находятся вне допустимого диапазона. Чаще всего под робастностью программных систем понимают именно устойчивость к некорректным данным. Система должна быть способна корректно обрабатывать такие данные путем выдачи соответствующих сообщений об ошибках, сбои и отказы системы на подобных данных недопустимы.
Для тестирования робастности к тестовым примерам, рассмотренным в предыдущем разделе, добавляются еще два тестовых примера:
проверяющие поведение системы за границей допустимого диапазона, а также в случае тестирования операций сравнения дополнительно дающие гарантию того, что в них не допущена опечатка.
Таким образом, если изобразить допустимый интервал как на Рис 4.1, то можно видеть, что для тестирования интервальных значений достаточно 7 тестовых примеров - пяти допустимых и двух на робастность.
(рис 4.1) Рекомендуемые проверочные значенияВ литературе часто встречается утверждение, что значение внутри интервала является избыточным и его тестирование не требуется. Однако, проверка внутреннего значения является полезной как минимум с психологической точки зрения, а также в случае, если интервал ограничен сложными граничными условиями. Также рекомендуется отдельно проверять значение 0 (даже если оно находится внутри интервала), т.к. зачастую это значение обрабатывается некорректно (например, в случае деления на 0).
При разработке тестовых примеров может возникнуть такая ситуация, в которой различные входные значения приводят к одним и тем же реакциям системы. Если при этом такие входные значения имеют что-то общее, то возможно объединение таких значений в классы эквивалентности, т.е. выполнение эквивалентного разбиения множества допустимых входных значений.
Разбиение на классы эквивалентности - это, в первую очередь, способ уменьшения необходимого числа тестовых примеров. Обычно, если в тест-требованиях специально не оговорено иное, при тестировании достаточно выполнить только один тестовый пример для каждого класса эквивалентности. Разбиение на классы эквивалентности особенно полезно, когда на вход системы может быть подано большое количество различных значений; тестирование каждого возможного значения привело бы к слишком большому объему тестирования.
Рассмотренные выше граничные условия могут служить примером классов эквивалентности:
Таким образом, тестирование граничных условий и робастности является частным случаем тестирования с использованием классов эквивалентности - вместо того, чтобы тестировать все недопустимые значения, выбираются только соседние с граничными.
При определении классов эквивалентности следует руководствоваться следующими правилами:
Другим примером разбиения на классы эквивалентности может служить тестирование открытия файла по его имени. В результате тестирования необходимо определить, все ли варианты имен обрабатываются системой согласно следующим тест-требованиям.
Входными значениями тестового примера являются различные имена файлов, выходными - реакция системы (ошибка или успешное открытие).
Можно выделить следующие классы эквивалентности:
По длине имени:
По символам:
Эти классы эквивалентности иллюстрируют, что проверки на границах интервалов применимы не только для тестирования арифметических операций и операций сравнения. Практически для любых данных, даже текстовых, можно определить "минимальные" и "максимальные" допустимые значения.
Разбиение на классы эквивалентности широко используется при тестировании корректности реализации арифметических операций и операций сравнения. Каждую операцию можно рассматривать как блок с входами - значениям и выходом - результатом операции. Для ее тестирования выполняется разбиение диапазона изменения переменных на входах блока на классы эквивалентности и методом анализа граничных значений этих переменных.
В таблице 4.1 приведены тестовые наборы для блоков, реализующих операции сравнения, в случае, когда на один из входов блока подается константа.
| greaterThan блок. Реализует операцию сравнения | greaterEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Вход a | b - d | b + d | b | min | max | Вход a | b - d | b + d | b | min | max |
| Выход | F | T | F | F | T | Выход | F | T | T | F | T |
| lessThan блок. Реализует операцию сравнения | lessEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
| Вход a | b - d | b + d | b | min | max | Вход a | b - d | b + d | b | min | max |
| Выход | T | F | F | T | F | Выход | T | F | T | T | F |
| equalTo блок. Реализует операцию сравнения | notEqualTo блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3 | 4 | № набора | 1 | 2 | 3 | 4 | ||
| Вход a | $$\ne$$ b | b | min | max | Вход a | $$\ne$$ b | b | min | max | ||
| Выход | F | T | F | F | Выход | T | F | T | T | ||
* тестовый набор реализуем, только если переменная на входе a - переменная целого типа
В приведенных тестовых наборах используются следующие обозначения:
d - шаг изменения ( resolution ) переменной на входе a. Если переменная на входе a - переменная целого типа, то d равно 1;min - минимальное значение переменной на входе a ;max - максимальное значение переменной на входе a.В таблице 4.2 приведены тестовые наборы для блоков, реализующих операции сравнения, в случае, когда на оба входа блока подаются переменные.
| greaterThan блок. Реализует операцию сравнения | greaterEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Вход a | val | val | val | min | max | Вход a | val | val | val | min | max |
| Вход b | val + d2 | val - d2 | val | max | min | Вход b | val + d2 | val - d2 | val | max | min |
| Выход | F | T | F | F | T | Выход | F | T | T | F | T |
| lessThan блок. Реализует операцию сравнения | lessEq блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3* | 4 | 5 | № набора | 1 | 2 | 3* | 4 | 5 |
| Вход a | val | val | val | min | max | Вход a | val | val | val | min | max |
| Вход b | val + d2 | val - d2 | val | max | min | Вход b | val + d2 | val - d2 | val | max | min |
| Выход | T | F | F | T | F | Выход | T | F | T | T | F |
| equalTo блок. Реализует операцию сравнения | notEqualTo блок. Реализует операцию сравнения | ||||||||||
| № набора | 1 | 2 | 3 | 4 | № набора | 1 | 2 | 3 | 4 | ||
| Вход a | val1 | val | min | max | Вход a | val1 | val | min | max | ||
| Вход b | val2 | val | max | min | Вход b | val2 | val | max | min | ||
| Выход | F | T | F | F | Выход | T | F | T | T | ||
* тестовый набор реализуем, только если переменные на входах блока - переменные целого типа
В приведенных тестовых наборах используются следующие обозначения:
d2 - шаг изменения ( resolution ) переменной на входе b. Если переменная на входе b - переменная целого типа, то d2 равно 1;val, val1, val2 - значения, которые взяты из середины диапазона, полученного при пересечении диапазонов переменных на входах a и b ;min - минимальное значение переменной на входе блока;max - максимальное значение переменной на входе блока.Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.