КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Проблема полноты тестирования
Тестовый план Тестирование обычно проводится снизу вверх, т.е. сначала тестируются отдельные функции, затем целые модули и далее проводится комплексное тестирование всей программы или комплекса программ. Для проведения тестирования разрабатывается тест план (test-plan) - совокупность тестовых наборов (примеров) (test-case). В каждом тестовом примере производится выполнение тестируемого программного элемента (SUT — software under test) при заданных Input - условиях и входных данных, и проверяются все Output - выходные данные на соответствия заданным значениям. Тестовый пример (набор) должен включать в себя как минимум: - входы (конкретные значения всех выходных параметров, все необходимые - ожидаемый выход (конкретные величины всех возвращаемых значений, все выводы в потоки, сигналы, все изменяемые свойства и установки окружения).
Кроме указанных данных удобно, если каждый тестовый пример имеет дополнительно: - номер (уникальный номер каждого тестового примера, чтобы на него можно было ссылаться); - ссылка на требование (если для тестирования используются требования, то указание ссылок на конкретные требования, которые проверяет данный тестовый пример, упростит локализацию ошибок и обеспечить возможность проверки полноты тестирования); - краткое описание (что проверяет данный тестовый пример).
Для проведения тестирования разрабатывается программа-драйвер (тест), который выполняет все тестовые примеры и сравнивает выходные значения с ожидаемыми. В результате выполнения теста получается не только общий результат – есть или нет ошибки, но еще и список пройденных и не пройденных тестовых примеров, который помогает локализовать ошибки в SUT. Для упрощения локализации ошибок и последующей модификации тест-плана, необходимо, чтобы тестовые примеры были независимы друг от друга, т.е. каждый последующий тестовый пример никак не использовал результаты работы предыдущего. Для этого необходимо провести установки всех начальных условий перед выполнением каждого тестового примера. Основная проблема тестирования ПО заключается в том, что проверить программу при всех возможных условиях функционирования в большинстве случаев невозможно. Это происходит либо в силу ограниченности ресурсов, либо в силу бесконечного количества возможных условий. Например, если рассмотреть функцию умножения двух рациональных чисел, варьируемых от -1000 до +1000, то в интервале от минимального возможного числа до максимального содержится бесконечное количество чисел. Т.е. все возможные значения входов проверить нельзя. Если же учесть, что машина оперирует не всеми этими числами, а различает только 10 знаков после запятой (т.е. множество чисел в интервале дискретно, минимальное отличие двух чисел 0,0000000001), то для проверки всех комбинаций из заданного диапазона понадобится 1023 тестовых примеров, что является достаточно большим числом для такой простой функции. Если проверяются не все возможные комбинации входных условий, то тестирование является не полным. В основном, для сложных программ, тестирование является не полным, но даже не полное тестирование может выявить большинство ошибок, если выработать грамотную стратегию их поиска. Часто используют метод деления входных значений на области эквивалентности, так, чтобы внутри каждой области для всех значений программа «вела себя» похоже. Тогда при написании тестовых примеров рассматриваются все значения на границах областей и по одному произвольному значению из каждой области (области определяются для каждого входного параметра). Такой подход называют методом 3х точек. Для приведенного выше примера функции умножения двух чисел, можно рассмотреть для каждого входа следующие области [-1000;0] и [0;+1000]. Такое деление образовано путем выявления всех особых точек (это точки -1000, 0 и +1000) и дальнейшим разбиением интервала множества значений этими точками. Такие точки называют критическими точками, в них тестируемая функция может менять свое поведение или потенциально вести себя особо. Т.е. для тестирования функции методом 3х точек достаточно проверить 5*5=25~102 случаев (для каждого входа это точки -1000; 0; 1000 и, например, -500 и 500), что значительно меньше полного перебора. Конечно, при таком подходе возможно, что какие-то ошибки останутся, но вероятность этого будет невелика и зависит от выбора критических точек. Функции, выполняющие различные сравнения, могут неверно его проводить, поэтому имеет смысл проверять их работу в непосредственно близости к критическим точкам. Для этого берутся значения, отстоящие от критических точек на величину дискретизации значений. Т.е. для примера функции умножения двух чисел, кроме значений метода 3х точек стоит рассмотреть значения -999,9999999999; -0,0000000001; 0,0000000001 и 999,9999999999. Этот подход называют методом 5ти точек. Иная сторона тестирования связана с типизацией переменных, при помощи которых задаются входные данные. Если для входных значений функции используются переменные типа float, а максимальное значение входа ограничено как +1000, то теоретически можно передать на вход и число +1001. Зачастую реакция функции на такое число не будет даже описана. Однако существуют приложения, чье поведение критично даже при передаче им входных значений, выходящих за пределы допустимых (например, авиационные программы). В этом случае подразумевается, что программа должна вести себя «корректно», т.е. не «зависнуть», не «повесить» систему, хотя выходное значение предсказать нельзя. Тестовые примеры, проверяющие поведение программы в таких случаях называются тестами на устойчивость (robustness). Если, при тестировании методом 5ти точек, проверять еще и значения выходящие за пределы допустимых диапазонов, то такой метод будет называться методом 7ми точек. В примере функции умножения двух чисел кроме значений -1000; -500; -999,9999999999; -0,0000000001; 0; 0,0000000001; 500; 999,9999999999; 1000 для каждого входа следует взять еще, например, значения -1001 и 100,0000000001. Как уже было сказано, для тестирования ПО необходимо обладать информацией о том, что оно должно делать. Это может быть либо подробное описание (требования), либо просто сам код программы (в этом случае подразумевается, что программа должна работать «корректно», не «портить память», не завершаться аварийно, не мешать другим процессам). В зависимости от исходной информации о ПО различают два подхода к тестированию – тестирование по требованиям и тестирование по коду. 7.3 Тестирование. Метод «черного ящика» Тестирование по требованиям или тестирование «черного ящика» подразумевает не использование сведений о структуре исходного кода. Все тестовые примеры составляются только на основе требований. Т.е. мы не видим, что находится внутри SUT и как оно работает. Единственное что доступно – это то, что SUT должно делать, внешние эффекты поведения программного обеспечения. При тестировании «черного ящика» удобно использовать методы 3х, 5ти или 7ми точек. При тестировании по требованиям используется понятие «покрытие» требований тестами. Тест покрывает требования, если он полностью проверяет выполнение каждого отдельного требования. При этом возможны как случаи, когда для проверки одного требования необходимо несколько тестовых примеров, так и случаи, когда достаточно одного тестового примера для проверки нескольких требований. Рассмотрим, например, следующее требование к функции умножения двух целых чисел: Если входные значения множителей выходят за границы диапазона [15.. 1500], то функция должна вернуть значение 0, в противном случае функция должна вернуть значение произведения двух множителей. Для тестирования этого требования (если быть точнее, то двух требований) необходимо проверить значения множителей как из диапазона [15.. 1500], так и вне его. Тесты для проверки значений из диапазона могут выглядеть, например, следующим образом: Таблица 3. Часть тест-плана
В таблице представлены все комбинации двух входов, каждый из которых принимает по три различных значения (метод трех точек), т.к. диапазон [15.. 1500] с точки зрения сложения чисел не имеет критических точек, кроме своих концов. Для проверки поведения функции за границами диапазона стоит проверить, например, значения 14 и 1501 для каждого входа. Таблица 3. Продолжение
Необходимо отметить, что не всегда можно покрыть все требования по тем же причинам, по которым невозможно добиться полноты тестирования, поэтому не всегда возможно доказать правильность работы программы. Кроме того, некоторые требования могут быть так сформулированы, что их нельзя протестировать. Например, требования могут касаться алгоритмов работы (а алгоритмы не видны при тестировании «черного ящика») или каких-нибудь внутренних переменных, не доступных извне. Некоторые требования вообще могут оказаться некорректными при тестировании, хотя могли быть интуитивно понятны при разработке, например требование «программа должна иметь дружественный интерфейс» – протестировать невозможно. Для исключения таких ситуаций необходимо уже на этапе составления требований формулировать их как можно более конкретно и однозначно. Метод тестирования по требованиям выявляет все несоответствия между запросами пользователя к ПО и поведением самого ПО. 7.4 Тестирование. Метод «белого ящика» Тестирование по коду или тестирование «белого ящика» основывается на проверке кода SUT, когда в ходе выполнения SUT проверяется выполнение каждого блока кода. При тестировании «белого ящика» основная задача – это выполнение всего кода, для проверки работоспособности всех его ветвей. При этом ставится задача покрытия кода тестами и рассматриваются разные уровни покрытия. Один из уровней покрытия (покрытие операторов) – это выполнение всех операторов. Т.е. при выполнении всех тестовых примеров в итоге должны выполниться все операторы (не в каждом тестовом примере, а по результатам выполнения всех тестовых примеров). Например, для кода (A,B,C – рассматриваются как входы). X = 0;
Достаточно одного тестового примера (ТП1: A=1, B=2, C=3). В этом случае выполнятся все операторы. Но если программист допустил ошибку и неверно написал условие, например так: if ((A<=B) || (A>B)) X = 5;
то тогда код будет работать неверно (переменной X всегда будет присваиваться значение 5), хотя показанный выше тестовый пример приведет к выполнению всех операторов и не выявит ошибки. Для выявления ошибок всегда требуется сначала выполнить уровень покрытия – по условиям. Он подразумевает, что покрытие покрытие по операторам целесообразно проводить только после того, как покрытие требований выполнено. Метод «белого ящика» используется и для дополнительных проверок всех условий на TRUE/FALSE. Т.е. каждое условие в ходе тестирования должно проверяться на оба возможных значения. Для покрытия по условиям приведенного примера кода необходимо уже два тестовых примера: ТП1: A=1, B=2, C=3; ТП2: A=3, B=2, C=3. Эти тестовые примеры позволяют найти ошибку. Существуют и другие виды покрытия [24,25,26]. Не все блоки кода всегда удается покрыть тестами. Это может быть связано с защитным программированием (когда входные значения функции проверяются на корректность, но передаются ей только корректные данные, т.к. передающая функция тоже проверяет их корректность); операторами выхода (закрывающаяся скобка «}» после оператора «exit»); мертвым кодом (код, которые заведомо никогда не выполняется). Проверка таких блоков кода и анализ их «безопасности» может происходить без выполнения самого кода, в этом случае группа экспертов читает и анализирует программный код, делая выводы о существовании или не существовании ошибок. Результаты подобного анализа могут установить причины появления непокрытого кода. Одной из причин может быть несовершенство тест-плана. Другой — пробелы в требованиях. И в том и в другом случае сам тестируемый код может не требовать изменений, но должны быть дописаны новые требования или изменены уже существующие и/или расширен тест-план. В обоих случаях процесс верификации повторяется.
Дата добавления: 2014-12-26; Просмотров: 1355; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |