Добавлен: 20.05.2023
Просмотров: 436
Скачиваний: 4
ВВЕДЕНИЕ
Главная цель программирования – это написание программы, но к сожалению, в большинстве случаев первый запуск приводит к ошибке. Какие же бывают ошибки?
Во-первых, программа может содержать синтаксические ошибки.
Во-вторых, при в воде корректных данных в программу, она может выдавать неправильный результат.
И в-третьих программа может неправильно реагировать на ввод некорректных данных.
Каким же образом нам найти ошибки в программе?
В этом поможет тестирование и отладка.
Отладка - это определение места ошибки и её исправление с использованием процессов выполнения его программ.
Тестирование - это процесс выполнения программ на некотором наборе данных, для которого заранее известен результат применения или известны правила поведения этих программ.
Цель тестирования обнаружение ситуации, при которой результаты работы программы не соответствуют ожидаемому. Но все ситуации проверить невозможно, из-за их огромного числа.
Для уменьшения количества тестов необходимо распределить на группы все возможные варианты выполнения программы. Существует два подхода: подход «черного ящика» и «белого ящика». Критерии черного ящика не учитывают внутреннее устройство программы. Критерии белого ящика учитывают.
В начале тестирования необходимо использовать подход черного ящика. Если их оказывается недостаточно, они дополняются тестами белого ящика.
Глава 1. МЕТОДЫ ТЕСТИРОВАНИЯ
1.1 Критерии черного ящика
Существуют следующие критерии черного ящика:
1) тестирование функций;
2) тестирование классов входных данных;
3) тестирование классов выходных данных;
4) тестирование области допустимых значений (тестирование границ класса);
5) тестирование длины набора данных;
6) тестирование упорядоченности набора данных.
Критерий тестирования функций актуален для многофункциональных программ. Он требует выполнения хотя бы одного теста для каждой из функций, реализуемых программой.
Критерий тестирования классов входных данных требует классифицировать входные данные, разделить их на классы таким образом, чтобы все данные из одного класса были равнозначны с точки зрения проверки правильности программы. Считается, что если программа работает правильно на одном наборе входных данных из этого класса, то она будет правильно работать на любом другом наборе данных из этого же класса. Критерий требует выполнения хотя бы одного теста для каждого класса входных данных.
Критерий тестирования классов выходных данных выглядит аналогично предыдущему критерию, только проверяются не входные данные, а выходные.
Надо отметить, что часто эти три критерия хорошо согласуются друг с другом. При применении одного из них остальные будут удовлетворены автоматически. Если программа реализует несколько функций, то вполне естественно, что каждой из этих функций будет соответствовать свой класс входных и свой класс выходных данных. Часто существует соответствие между классами входных и выходных данных.
Тестирование области допустимых значений (тестирование границ класса). Если область допустимых значений переменной представляет собой простое перечисление (например, имена, цвет, пол т. п.), надо проверить, что программа правильно понимает все эти значения и не принимает вместо них никаких иных значений. Например, как программа отреагирует на попытку ввести несуществующую пол.
Если класс допустимых значений представляет собой числовой диапазон, то понадобится более серьезная проверка. В этом случае выделяются:
1) нормальные условия (в середине класса);
2) граничные (экстремальные) условия;
3) исключительные условия (выход за границу класса).
Например, пусть программа предназначена для обработки деканатом информации об одной студенческой группе. Нормальное число студентов в группе — 20–25. Но на младших курсах их часто бывает больше, а на старших — наоборот. Пусть максимальное число студентов в группе ограничено 30. В этом случае имеет смысл проверить правильность работы программы с группой из 20 студентов (нормальные условия), с группой из 30 человек и с группой из одного человека (экстремальные условия). Необходимо проверить, как программа отреагирует на приказ о зачислении в группу 31-го студента и об отчислении последнего остававшегося в группе студента (исключительные условия).
Иногда требуется более тонкая градация. Возможна ситуация, когда вся область допустимых значений делится на отдельные подобласти, требующие от программы разной реакции. Например, пусть программа готовит сводный отчет о работе некоторой фирмы. Известно, что ежедневно фирма продает продукции примерно на 30 тыс. руб. Как должна реагировать программа на сообщение о том, что доход за день 10 руб. или 1 000 000руб.? Теоретически можно допустить, что этот фирма сработала настолько плохо или, наоборот, потрясающе. Однако логично предположить, что в этих случаях при вводе данных возникли проблемы или данные введены не верно? В результате вся область допустимых значений делится на 3 подобласти: подобласть нормальных значений, подобласть подозрительно больших значений и подобласть подозрительно маленьких значений. Нормальные данные программа должна просто обрабатывать. Подозрительно большие и подозрительно малые значения допустимы, однако при их получении имеет смысл затребовать подтверждения, действительно ли все верно введено.
Впрочем, вместо деления области допустимых значений на подобласти можно было бы просто выделить 3 класса входных данных: нормальные, слишком маленькие, слишком большие.
Следующие два критерия являются частными случаями, уточняющими ранее названные критерии. Но в силу их важности и частой применимости, они заслуживают отдельного упоминания.
Тестирование длины набора данных можно считать частным случаем тестирования области допустимых значений. В данном случае речь пойдет о допустимом количестве элементов в наборе. Если программа последовательно обрабатывает элементы некоторого набора данных, имеет смысл проверить следующие ситуации:
1) пустой набор (не содержит ни одного элемента);
2) единичный набор (состоит из одного-единственного элемента);
3) слишком короткий набор (если предусмотрена минимально допустимая длина);
4) набор минимально возможной длины (если предусмотрено);
5) нормальный набор (состоит из нескольких элементов);
6) набор из нескольких частей (если такое возможно. Например, если программа читает литеры из текстового файла или печатает текст, то как она отнесется к переходу на следующую строку? На следующую страницу?);
7) набор максимально возможной длины (если предусмотрено);
8) слишком длинный набор (с длиной больше максимально допустимой).
Тестирование упорядоченности входных данных важно для задач сортировки и поиска экстремумов. В этом случае имеет смысл проверить следующие ситуации (классы входных данных):
1) данные неупорядочены;
2) данные упорядочены в прямом порядке;
3) данные упорядочены в обратном порядке;
4) в наборе имеются повторяющиеся значения;
5) экстремальное значение находится в середине набора;
6) экстремальное значение находится в начале набора;
7) экстремальное значение находится в конце набора;
8) в наборе несколько совпадающих экстремальных значений.
1.2 Критерии белого ящика
Первый из критериев белого ящика — критерий покрытия операторов. Он требует подобрать такой набор тестов, чтобы каждый оператор в программе выполнился хотя бы один раз.
В качестве примера рассмотрим следующий фрагмент Java программы:
Пример 1
a = 0;
if (x>3) {a = 10;}
b =1/a;
Для того чтобы удовлетворить критерию покрытия операторов, достаточно одного выполнения. Такого, чтобы x был больше 3. Очевидно, что ошибка в программе этим тестом обнаружена, не будет. Она проявится как раз в том случае, когда x <= 3. Но такого теста критерий покрытия операторов от нас не требует.
Итак, мы имеем программу, оттестированную с точки зрения критерия покрытия операторов и при этом содержащую ошибку.
Следуя критерию покрытия операторов, мы проверили только положительную ветвь развилки, но не затронули отрицательную.
Чтобы избавиться от указанного недостатка, введем второй критерий белого ящика — критерий покрытия ветвей (иначе его называют критерием покрытия решений). Он требует подобрать такой набор тестов, чтобы каждая ветвь в программе была выполнена хотя бы один раз. Тестирование с точки зрения этого критерия обнаружит ошибку в предыдущем примере.
Рассмотрим другой пример. На Java он будет выглядеть
так:
Пример 2
a = 7;
while (a>x) { a--;}
b = 1/a;
Для того чтобы удовлетворить критерию покрытия ветвей, в данном случае достаточно одного теста. Например, чтобы x был равен 6 или 5. Все ветви программы будут пройдены (при x = 5 одна из ветвей — тело цикла — даже 2 раза). Но ошибка в программе обнаружена так и не будет! Она проявится в одном-единственном случае, когда x = 0. Но такого теста от
нас критерий покрытия ветвей не потребовал.
Итак, мы имеем программу, оттестированную с точки зрения критерия покрытия ветвей и при этом содержащую ошибку. Причина в том, что некоторые ветви в программе могут быть пройдены несколько раз, и результат выполнения зависит от количества проходов. Для того чтобы учесть этот факт, введем третий критерий белого ящика — критерий покрытия путей. Он требует подобрать такой набор тестов, чтобы каждый
путь в программе был выполнен хотя бы один раз. Тестирование с точки зрения этого критерия обнаружило бы ошибку в примере 2. Но из этого же примера виден принципиальный недостаток данного критерия. В примере 2 возможно бесконечно много путей. Проверить их все невозможно. Значит, как только в программе появляются циклы с пред- или постусловием или цикл со счетчиком, но с вычисляемыми границами, количество путей в программе становится потенциально бесконечным, и критерий покрытия путей становится неприменимым. Необходим какой-то компромиссный критерий. Более жесткий, чем покрытие ветвей, но менее жесткий, чем
покрытие путей.
Кроме проблем с проверкой циклов существенные проблемы связаны с проверкой сложных условий — логических выражений, содержащих знаки дизъюнкции и/или конъюнкции.
Например:
if (a<b || c=0) {..}
while (i<=n && x>exp) {..}
И в том, и в другом операторе можно пройти по обеим ветвям, изменяя значение только одного из простых условий. Пусть c не равно 0. Меняя значение переменных a и b, можно пройти и по ветви «то», и по ветви «иначе». При этом ситуация, когда c = 0, останется непроверенной. Аналогично, пусть i <= n. Меняя значения переменных x и eps, можно управлять выполнением цикла while, не проверив поведение программы при i > n. Для того чтобы учесть подобные ситуации, были предложены следующие критерии:
-критерий покрытия условий;
-критерий покрытия решений/условий;
-критерий комбинаторного покрытия условий.
Критерий покрытия условий требует набор тестов, при котором каждое простое условие (слагаемое в дизъюнкции и сомножитель в конъюнкции) получит и значение «истина», и значение «ложь» хотя бы один раз.
Критерий пытается «в лоб» исправить вышеуказанный недостаток в тестировании сложных условий. Однако сам оказывается весьма слаб. Дело в том, что выполнение критерия покрытия условий не гарантирует покрытие ветвей. Пусть сложное условие представляет собой дизъюнкцию двух слагаемых.
Например:
If (a>0 || c=0) { d=1;} else { d= 1/c;}
При первом выполнении первое слагаемое истинно, второе ложно, вся дизъюнкция истинна. При втором выполнении первое слагаемое ложно, второе истинно, вся дизъюнкция истинна. Критерий покрытия условий выполнен, критерий покрытия ветвей — нет. Ошибка не найдена.
Чтобы исправить этот недостаток, критерии покрытия ветвей (решений) и условий объединяют в единый критерий покрытия решений/условий. Он требует набор тестов, при котом каждая ветвь в программе была пройдена хотя бы один раз и чтобы каждое простое условие (слагаемое в дизъюнкции и сомножитель в конъюнкции) получило и значение «истина», и значение «ложь» хотя бы один раз. Критерий надежнее, чем простое покрытие ветвей, но сохраняет его принципиальный недостаток: плохую проверку циклов. Приведенный выше пример 2, «ломающий» критерий покрытия ветвей, «сломает» и критерий покрытия решений/условий. Ошибка в данном случае проявится только при фиксированном количестве повторений цикла (в примере 2 — семикратном), а критерий покрытия решений/условий не гарантирует, что повторений будет именно столько.
Совмещение критериев покрытия ветвей и покрытия условий не решает также всех проблем, порождаемых сложными условиями. Ошибки могут быть связаны не со значением того или иного простого условия, а с их комбинацией.
Пример 3
if (a=0 || b=0 || c=0) { d= 1/(a+b); }
else {d = 1;}
В примере 3 шибка будет выявлена только при одновременном равенстве нулю двух переменных: a и b. Критерий покрытия решений/условий не гарантирует проверки такой ситуации.
Для решения данной проблемы был предложен критерий комбинаторного покрытия условий, который требует такой набор тестов, чтобы хотя бы один раз выполнялась любая комбинация простых условий. Критерий значительно более надежен, чем покрытие решений/условий, но обладает двумя существенными недостатками. Во-первых, он может потребовать очень большого числа тестов. Количество тестов, необходимых для проверки комбинации n простых условий, равно 2 в степени n . Комбинация двух условий потребует четырех тестов, трех условий - восьми, четырех условий - шестнадцати тестов и т. д. Во-вторых, даже комбинаторное покрытие условий не гарантирует надежную проверку циклов. Тот же самый пример 2, который демонстрировал недостатки покрытия ветвей и покрытия решений/условий, покажет и недостаток комбинаторного покрытия условий.