ЖИЗНЕННЫЙ ЦИКЛ ПРОГРАММ. ТЕСТИРОВАНИЕ

Машинная программа выполняет то, что
Вы ей приказали делать, а не то, что
Вы бы хотели, чтобы она делала
«Закон Мерфи». Артур Блох
Вот Вы и закончили набирать написанную программу! С чувством глу-
бокого удовлетворения и ощущением законченности работы Вы решаете за-
пустить программу на счет (ну так, лишь чтобы убедиться, что все ОК).
Тут то все и начинается… Итак, программа не работает. Что же делать
дальше? Необходимо «найти и обезвредить» ошибку. О том, как это сде-
лать, мы и расскажем в нашей статье.
Обычно лишь начинающие программисты всегда уверены в правиль-
ности своих программ. Среди опытных программистов бытует афоризм: «В
каждой программе есть хотя бы одна ошибка». Ошибки могут быть разного
характера — синтаксические, (т.е. нарушены правила записи операторов)
или семантические (т.е. ошибки в структуре алгоритма). Что касается
синтаксических ошибок, то их помогает выявить транслятор. В процессе
трансляции программы он в случае обнаружения такой ошибки выдает на
экран соответствующее сообщение, указав код ошибки и место ее располо-
жения. Будем считать, что ошибки этого типа, отлавливаемые транслято-
ром, для Вас не представляют особых проблем, и что проверка синтаксиса
в конце концов прошла успешно.
После того как все синтаксические ошибки устранены, еще ни в коем
случае нельзя быть уверенным, что программа составлена правильно. Это
подобно тому, как безошибочное с точки зрения орфографии школьное со-
чинение далеко не всегда правильно раскрывает его тему. Поэтому необ-
ходимо еще выявить и устранить все ошибки в самом алгоритме. К тому же
мы не всегда, к сожалению, можем знать заранее, каким, хотя бы прибли-
зительно должен быть результат работы программы, поэтому, даже если
программа не содержит синтаксических ошибок, ее все равно обязательно
нужно отлаживать.
После запуска программы на выполнение, по классификации книги
/1/, возможны следующие варианты:
1. (Лучший вариант) Программа выдает некоторые результаты, кото-
рые «не лезут ни в какие ворота».
2. (Средний вариант) Программа аварийно останавливается — деление
на 0, переполнение и т.п.
3. (Худший вариант) Программа зацикливается, не выдавая никаких
сообщений.

— 2 —
4. (Наихудший вариант) Программа работает. Она выдает похожие на
истину, но неверные результаты, и мы начинаем ими пользоваться без
проверки.
5. (Маловероятный вариант) Программа работает правильно. Вследс-
твие редкости появления этот вариант не рассматривается.
В случаях 1-4 мы приходим к выводу, что в программе есть смысло-
вая (семантическая) ошибка. (Альтернативный вариант умозаключения —
«Интересно, и почему это машина работает неправильно» не является
конструктивным и мы его рассматривать не будем). Семантическая (смыс-
ловая) ошибка означает, что Вы применили операторы, которые не дают
нужного эффекта. Например, Вы вместо a*b написали a-b. Ошибки такого
типа транслятор выловить самостоятельно, естественно не может, поэтому
придется заняться этим вопросом Вам. Чем раньше будут выявлены ошибки,
тем меньше вреда они принесут. Известно, например, что запуск первой
американской станции к Венере оказался неудачным вследствие ошибки в
программе.
Можно выделить два основных этапа работы с ошибками: тестирование
и отладка. Рассмотрим первый этап.
Тестирование — это процесс выполнения программы на
тестах, с целью определения, существуют ли в ней ошибки. Тесты — это
различные варианты входных данных, для которых мы заранее знаем, какой
результат должна выдавать программа. Не существует методов тестирова-
ния, применение которых гарантировало бы выявление всех ошибок. Одна-
ко, имеется целый ряд способов и рекомендаций по построению и исполь-
зованию тестов. Рассмотрим основные методы, используемые на практике.
Итак, как готовить тесты? Рассмотрим метод так называемого «черного»
ящика
.
При использовании этого метода предполагают, что программа — это
как бы «черный» ящик. Вспомните такой же ящик в популярной игре «Что?
Где? Когда?». Как и Знатоки, мы не знаем, что «лежит» в этом ящике, то
есть, как работает тестируемая программа. Но мы знаем, что она должна
делать.
Если реакция программы соответствует ожидаемой, то можно сказать,
что на данных тестах программа работает правильно. Однако, сколько бы
тестов мы не разработали, все равно нет полной уверенности, что наша
программа работает правильно, т.к. тестирование не может гарантировать
отсутствие всех ошибок. Поэтому при разработке тестов необходимо за-
даться целью обнаружить как можно больше ошибок на минимальном мно-
жестве тестов.
Выделим следующие способы построения тестов с помощью метода
«черного» ящика.
А. Анализ граничных условий. Хорошая программа должна проверять

— 3 —
входные данные, не являются ли они больше или меньше допустимых по ус-
ловию задачи значений — граничных условий. Как показал опыт школьных и
студенческих олимпиад по информатике, программы многих участников сра-
зу же «спотыкаются» на таких тестах. Приведем основные рекомендации —
какого типа тесты рассматривать в данном случае.
1. Построить тесты для минимального и максимального допустимых
значений условия. Например, если данные х должны принадлежать отрезку
[a,b], то включить в множество тестов: x=a и х=b.
2. Построить тесты, выходящие за пределы граничных условий. Нап-
ример, для отрезка [a,b] тесты xb.
3. Построить тесты для значений, выходящих за пределы заданных
граничных условий с незначительным отклонением. Например, x=a-0.001;
y=b+0.001.Заметим, что в этом случае необходимо хорошо анализировать
результаты, т.к. тестируемая программа может за счет погрешности вы-
числений и округления считать, что x,y принадлежат отрезку [a,b].
4. Если входные или выходные данные представляют собой некоторый
упорядоченный набор (например, по возрастанию, убыванию или алфавиту),
то выделить первый и последний элементы набора и проверить, соответс-
твуют ли они правилам упорядочения. Так, если на вход поступают фами-
лии, которые должны быть упорядочены по алфавиту:» Иванов, Петров, Си-
доров», то проверить, будет ли «Иванов» начинаться с буквы, стоящей в
алфавите раньше, чем первая буква последней фамилии («Сидоров»). Можно
также для всех входных данных проверить свойство упорядоченности.
5. Проверить границы результатов получившихся при вычислении (об-
ласть допустимых значений). Например, при вычислении sin(x) мы должны
получить результаты, удовлетворяющие условию |sin(x)|<=1. Если на ка- ких-то входных данных программа получает значение |sin(x)| большее 1, значит алгоритм программы содержит ошибки. 6. Если входные данные программы выбираются из файла (списка, массива, строки) то сначала необходимо проверить, не будет ли этот файл (список, массив, строка) пуст. 7. Если множество граничных условий представляет собой несколько несовпадающих областей (например, отрезки [a,b], [c,d], [e,f]), то вы- полнить шаги 1- 6 для каждой такой области. В. Построение эквивалентных разбиений. Мы уже говорили, что не-
возможно построить полный набор тестов, т.е. такой набор, при котором
были бы обнаружены все ошибки в программе. Но можно попытаться разбить
область определения входных данных на несколько множеств M1,
M2,…,Mn. Эти множества называются 1 множествами эквивалентности. 0 Расс-
мотрим основные правила для формирования множеств эквивалентных разби-
ений.
1. Если в условии задачи указывается количество входных значений,

— 4 —
то необходимо сформировать три множества: для совпадающего с заданным,
меньшего и большего заданного.
2. Если в условии задачи указывается (явно или неявно) область
определения входных данных (например, отрезок [a,b]), то формируется
три множества эквивалентности: для правильных данных (x принадлежит
[a,b]) и для данных, выходящих за пределы этой области (x<a; x>b).
3. Если в условии задачи сформировано некоторое конкретное усло-
вие (например, что входные данные должны быть натуральными числами),
то формируются множества как с правильными входными данными (натураль-
ные числа), так и с неправильными (например, неположительные, нецелые
числа).
4. Если в задаче заданы конкретные условия выбора (например,
подсчитать количество груш и яблок), то необходимо сформировать мно-
жества, как совпадающие с заданными условиями, так и не совпадающие с
ними (то есть, сформировать множества: «груши», «яблоки» и других зна-
чения — «не груши и не яблоки»).
5. При необходимости каждое множество эквивалентности можно раз-
бить по правилам 1-4 на более мелкие подмножества.
Заметим, что множества граничных условий — это множества эквива-
лентности, которые позволяют исследовать поведение программы на и ря-
дом с границами области определения.
В литературе по вопросам тестирования программ /4, 5/ встречаются
и другие методы. Но на начальном этапе можно остановиться на приведен-
ных ниже.
Рассмотрим пример, показывающий, как применяются вашеназванные
методы при проектировании тестов. За основу возьмем задачу из работы
/4/, несколько модифицировав ее.
З а д а ч а . Даны три числа a,b,c, являющиеся сторонами треуголь-
ника. Найти его площадь. Данные числа вводятся из файла.
Схема построения тестов по методу «черного» ящика.
Разобьем на множества эквивалентности:
а) в условии указано три числа, следовательно, необходимо постро-
ить три множества разбиений: M1- в файле меньше трех чисел; M2 — в
файле три числа; M3 — в файле больше трех чисел.
б) в условии указано, что эти три числа являются сторонами треу-
гольника, следовательно, они не могут быть равными нули или отрица-
тельными. Определим верхнюю границу области определения. Так как в ус-
ловии она явно не указана, будем исходить из возможностей нашего
компьютера и языка программирования. Таким образом неявно из условия
задачи мы определили область допустимых значений (A;B] и потом выде-
лить три основных множества: M4 — a, b, c из (A;B], где A=0; M5 — a,
b, c <= A; M6 — a, b, c > B;

— 5 —
в) в условии задачи сформулировано условие, что числа a, b и с
являются сторонами треугольника; следовательно, согласно правилам гео-
метрии, они должны удовлетворять следующему свойству: «сумма длин двух
любых сторон треугольника должна быть больше третьей стороны». Следо-
вательно, мы должны сформировать множества разбиений: M7 — где числа
a, b, c удовлетворяют данному условию; M8 — где числа a, b, c — не
удовлетворяют данному условию;
г) результат вычисления, т.е. выходные данные программы должен
принадлежать области допустимых значений. Так как площадь треугольни-
ка, как и выходные данные a, b, c является неотрицательным числом из
промежутка (A;B], то необходимо сформировать множества для площади: M9
— где значение площади принадлежит (A;B]; M10 — где значение площади
не принадлежит (A;B].
Заметим, что пересечение множеств M1,M2,…, Mn может быть не
пусто. Например, тест с числами a, b, с; где a, b, c — действительно
являются сторонами треугольника, а площадь принадлежит (A;B) может
присутствовать в множествах M1, M4, M7, M9. Поэтому такой текст можно
включить только в M1, а в остальные множества не включать. Граничные
условия покрываются данным множеством разбиений и будут вводить конк-
ретно во все эти множества.
Как вы видите, такая, казалось бы простая задача, потребовала от
нас столько тестов из 10 множеств эквивалентных разбиений. И все рав-
но, может возникнуть ситуация, при которой ни один из перечисленных
тестов не поможет обнаружить ошибку в программе, а она все-таки будет
там присутствовать.
Подбор тестов следует осуществлять таким образом, чтобы на сово-
купности тестов каждая ветвь (и каждый оператор) программы была бы вы-
полнена хоты бы один раз. Этот метод называется 3методом покрывающих
3путей.
Для конструирования теста можно пойти обратным путем — от резуль-
тата к входным данным. Например для того, чтобы получить коэффициенты
уравнения 4-го порядка, имеющего ровно 2 корня, можно взять произволь-
ные числа a и b и трехчлен (x-a)(x-b) умножить на выражение (x*x+1),
не имеющее вещественных корней.
В любой программе есть, как правило, разветвления, переходы, цик-
лы. Необходимо проверить как логическую структуру программы, так и вы-
числения на линейных участках программы, т.е. там, где операторы вы-
полняются последовательно один за другим в порядке их записи.
Проверив с помощью тестов все линейные участки программы, можно
приступить к проверке логической структуры программы, т.е. правильнос-
ти организации циклов, ветвлений, переходов. Рассмотрим, как это вы-
полняется, на примере следующей задачи.

— 6 —
Задача. Составить программу нахождения корней квадратного уравне-
ния ax +bx+c=0. Коэффициенты уравнения последовательно вводятся в па-
мять ЭВМ.
Программа вычисления:
{A,B,C — коэффициенты для вычисления корней квадратного уравнения}
if A=0 then
if B=0 then
if C=0 {вариант — С=0, В=0, А=0}
then writeln (‘корни — любые вещественные числа’)
else {вариант — С-любое, В=0, А=0}
writeln (‘нет решения’)
else begin {вариант — A=0, С-любое, В — не равно 0}
x1:=-C/B;
writeln (‘X1=X2=’,X1)
end
else begin {вариант — С-любое, В — любое, А не равно 0}
D:=B*B-4*A*C; {Дискриминант}
if D

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>