Целью тестирования является обнаружение ошибок в программе.
Тестирование программного обеспечения охватывает целый ряд видов деятельности, аналогичных последовательности процессов разработки программного обеспечения. В него входят/1:
а) постановка задачи для теста,
б) проектирование теста,
в) написание тестов,
г) тестирование тестов,
д) выполнение тестов,
е) изучение результатов тестирования.
Решающую роль играет проектирование тестов. Возможен целый ряд подходов к стратегии проектирования тестов. Чтобы ориентироваться в них, рассмотрим два крайних подхода /1/. Первый состоит в том, что тесты проектируются на основе внешних спецификаций программ и модулей, либо спецификаций сопряжения программы или модуля. Программа при этом рассматривается как черный ящик (стратегия ‘черного ящика’). Существо такого подхода – проверить соответствует ли программа внешним спецификациям. При этом логика модуля совершенно не принимается во внимание.
Второй подход основан на анализе логики программы (стратегия ‘белого ящика’). Существо подхода – в проверке каждого пути, каждой ветви алгоритма. При этом внешняя спецификация во внимание не принимается.
Альтернатива ‘черный ящик’ – ‘белый ящик’ является достаточно общей, что подтверждается ее применением не только при тестировании, но, например, в альтернативных направлениях исследований в области искусственного интеллекта.
Тестирование по принципу белого ящика характеризуется степенью, какой тесты выполняют или покрывают логику (исходный текст программы).
Если отказаться полностью от тестирования всех путей, можно показать, что критерием покрытия является выполнение каждого оператора программы хотя бы один раз.
Это необходимое, но недостаточное условие для приемлемого тестирования по принципу белого ящика.
В этой программе можно выполнить каждый оператор, записав один единственный тест, который реализовал бы путь ace. Т.е., если бы на входе было: А=2, В=0, Х=3, каждый оператор выполнился бы один раз. Но этот критерий на самом деле хуже, чем он кажется на первый взгляд. Пусть в первом условии вместо “and”®“or” и во втором вместо “x>1”®“x<1” (блок-схема правильной программы приведена на рисунке 15.2, а неправильной – на рисунке 15.3). Результаты тестирования приведены в таблице 15.1. Обратите внимание: ожидаемый результат определяется по алгоритму на рисунке 15.2, а фактический – по алгоритму рисунка 15.3, поскольку определяется чувствительность метода тестирования к ошибкам программирования. Как видно из этой таблицы, ни одна из этих ошибок не будет обнаружена .
Таблица 15.1 – Результат тестирования методом покрытия операторов
| Тест | Ожидаемый результат | Фактический результат | Результат тестирования |
| A=2, B=0, X=3 | X=2,5 | X=2,5 | неуспешно |
Метод покрытия решений (покрытия переходов)
Более сильный метод тестирования известен как покрытие решений (покрытие переходов).
Согласно данному методу должно быть написано достаточное число тестов, такое, что каждое направление перехода должно быть реализовано по крайней мере один раз.
Покрытие решений обычно удовлетворяет критерию покрытия операторов. Поскольку каждый оператор лежит на некотором пути, исходящем либо из оператора перехода, либо из точки входа программы, при выполнении каждого направления перехода каждый оператор должен быть выполнен.
Для программы приведенной на рисунке 15.3 покрытие решений может быть выполнено двумя тестами, покрывающими пути {ace, abd}, либо {aсd,abe}. Пути {aсd,abe}покроим, выбрав следующие исходные данные: {A=3, B=0, X=3} и {A=2, B=1, X=1} (результаты тестирования – в таблице 15.2).
Таблица 15.2 – Результат тестирования методом покрытия решений
| Тест | Ожидаемый результат | Фактический результат | Результат тестирования |
| A=3, B=0, X=3 | X=1 | X=1 | неуспешно |
| А=2, В=1, Х=1 | Х=2 | Х=1,5 | Успешно |
Метод покрытия условий
Лучшие результаты по сравнению с предыдущими может дать метод покрытия условий. В этом случае записывается число тестов, достаточное для того, чтобы все возможные результаты каждого условия в решении выполнялись по крайней мере один раз.
В предыдущем примере имеем четыре условия: {A>1, B=0}, {A=2, X>1}. Cледовательно, требуется достаточное число тестов, такое, чтобы реализовать ситуации, где A>1, A£1, B=0 и B¹0 в точке а и A=2, A¹2, X>1 и X£1 в точке В. Тесты, удовлетворяющие критерию покрытия условий и соответствующие им пути:
а) A=2, B=0, X=4 ace
б) A=1, B=1, X=0 abd
Таблица 2.3 – Результаты тестирования методом покрытия условий
| Тест | Ожидаемый результат | Фактический результат | Результат тестирования |
| A=2, B=0, X=4 | X=3 | X=3 | неуспешно |
| A=1, B=1, X=0 | X=0 | X=1 | успешно |
Критерий решений (условий)
Критерий покрытия решений/условий требует такого достаточного набора тестов, чтобы все возможные результаты каждого условия в решении выполнялись по крайней мере один раз, все результаты каждого решения выполнялись по крайней мере один раз и, кроме того, каждой точке входа передавалось управление по крайней мере один раз.
Два теста метода покрытия условий
а) A=2, B=0, X=4 ace
б) A=1, B=1, X=0 abd
отвечают и критерию покрытия решений/условий. Это является следствием того, что одни условия приведенных решений скрывают другие условия в этих решениях. Так, если условие А>1 будет ложным, транслятор может не проверять условия В=0, поскольку при любом результате условия В=0, результат решения ((А>1)&(В=0)) примет значение ложь. Следовательно, недостатком критерия покрытия решений/условий является невозможность его применения для выполнения всех результатов всех условий.
Другая реализация рассматриваемого примера приведена на рисунке 15.4. Многоусловные решения исходной программы разбиты на отдельные решения и переходы. Наиболее полное покрытие тестами в этом случае выполняется так, чтобы выполнялись все возможные результаты каждого простого решения. Для этого нужно покрыть пути HILP (тест А=2,В=0,Х=4), HIMKT (тест А=3, В=1, Х=0), HJKT (тест А=0, В=0, Х=0), HJKR (тест А=0, В=0, Х=2)..
Протестировав алгоритм на рисунке 2.4, нетрудно убедиться в том, что критерии покрытия условий и критерии покрытия решений/условий недостаточно чувствительны к ошибкам в логических выражениях.
Метод эквивалентного разбиения
Поскольку, как было обосновано выше, исчерпывающее входное тестирование программы невозможно, реально приходится ограничиваться использованием небольшого подмножества всех возможных входных данных. Исходя из этого:
а) каждый тест должен включать столько различных входных условий, сколько это возможно, с тем чтобы минимизировать общее число тестов;
б) необходимо пытаться разбить входную область программы на конечное число классов эквивалентности так, чтобы можно было предположить, что каждый тест, являющийся представителем некоторого класса, эквивалентен любому другому тесту этого класса. Другими словами, если один тест класса эквивалентности обнаруживает ошибку, то следует ожидать, что и все другие тесты этого класса эквивалентности будут обнаруживать эту ошибку. И наоборот, если тест не обнаруживает ошибки, то следует ожидать, что ни один тест этого класса эквивалентности не будет обнаруживать ошибки.
Эти два положения составляют основу методологии тестирования по стратегии “черного ящика”, известного как эквивалентное разбиение/1/. Второе положение используется для разработки набора “интересных условий”, которые должны быть протестированы, а первое – для разработки минимального набора тестов, покрывающих эти условия.
Разработка тестов методом эквивалентного разбиения осуществляется в два этапа:
а) выделение класса эквивалентности;
б)построение тестов.
Выделение классов эквивалентности
Классы эквивалентности выделяются путем выбора каждого входного условия (обычно это предложение или фраза в спецификации) и разбиением его на две или более групп. Для выполнения этой операции используют таблицу следующего вида:
Таблица 15.5 – Заголовок таблицы для определения классов эквивалентности
| Входные условия | Правильные классы эквивалентности | Неправильные классы эквивалентности |
Здесь – правильные классы эквивалентности соответствуют правильным входным данным программы, а неправильные классы эквивалентности представляют все другие возможные состояния входных условий.
Если задаться входными или внешними условиями, то выделение классов эквивалентности представляет собой в значительной степени эвристический процесс. При этом существует ряд правил:
- если входное условие описывает область значений, например, “целое данное может принимать значения от 1 до 999, то определяется один правильный класс эквивалентности (1£значение целого данного £ 999) и два неправильных (значение целого данного<1 и значение целого данного>999);
- если входное условие описывает число значений, например, “в автомобиле могут ехать от одного до шести человек”, то определяется один правильный класс эквивалентности и два неправильных (ни одного и более 6 человек);
- если входное условие описывает множество входных значений (например, “известными способами передвижения являются: на автобусе, грузовике, такси, пешком или на мотоцикле”), то определяются правильные классы эквивалентности для каждого значения (таких “правильных” классов эквивалентности должно быть 5, по числу возможных способов передвижения) и один неправильный (например, “на прицепе”);
- если входное условие описывает ситуацию “должно быть” (например, “первым символом идентификатора должна быть буква”), то определяется один правильный класс эквивалентности (первый символ – буква) и один неправильный (первый символ – не буква);
- если есть основания считать, что различные элементы класса эквивалентности воспринимаются программой неодинаково, то данный класс эквивалентности разбивается на меньшие классы эквивалентности.
Построение тестов
Этот процесс включает в себя:
- назначение каждому классу эквивалентности уникального номера;
- проектирование новых тестов, каждый из которых покрывает как можно большее число непокрытых правильных классов эквивалентности, пока все правильные классы эквивалентности не будут покрыты тестами;
- запись тестов, каждый из которых покрывает один и только один из непокрытых неправильных классов эквивалентности. Каждый неправильный класс эквивалентности должен быть покрыт индивидуальным тестом. Причина этого состоит в том, что определенные проверки с ошибочными входами скрывают или заменяют другие ошибки с ошибочными входами.
Метод анализа граничных значений
Опыт показывает, что тесты, исследующие граничные условия, приносят большую пользу чем тесты, которые их не исследуют.
Определение: граничные условия это ситуации, возникающие непосредственно на, выше или ниже границ входных и выходных классов эквивалентности.
Анализ граничных значений отличается от эквивалентного разбиения в двух отношениях:
- выбор любого элемента в классе эквивалентности в качестве представительного при анализе граничных значений осуществляется таким образом, чтобы проверить тестом каждую границу этого класса.
- при разработке тестов рассматривают не только входные условия (пространство входов), но и пространство результатов, то есть выходные классы эквивалентности.
Метод анализа граничных значений требует творческого подхода и специализации. Приведем несколько общих правил этого метода /1/.
Построить тесты для границ области и тесты с неправильными входными данными для ситуаций незначительного выхода за границы области, если входное условие описывает область значений.
Пример: если правильная область входных значений есть -1.0 ¸ 1.0, то следует написать тесты для ситуаций: -1.0; 1.0; -1.001; 1.001.
Если входные условия удовлетворяют дискретному ряду значений, построить тесты для минимального и максимального значений условий и тесты для значений входных условий больших или меньших максимального и минимального.
Пример: если входной файл может содержать от 1 до 255 записей, то построить тесты для 0; 1; 255; 256 записей.
Использовать правило 1 для каждого входного условия.
Пример: Программа вычисляет ежемесячный расход. Минимум расхода составляет 0.00, а максимум – 1165.25 (усл. ед.). Нужно построить тесты для отрицательного расхода и расхода, большего 1165.25.
Важно проверить границы пространства результатов, поскольку не всегда границы входных областей представляют такой же набор условий, как и границы выходных областей. Пример: y=sin(x); xÎ[0;p]
Не всегда можно получить результат вне выходной области, но, тем не менее, стоит рассмотреть эту возможность.
Построить тесты для минимального и максимального значения каждого выходного условия, а также тесты, обеспечивающие нарушение границ выходных условий.
Пример: система информационного поиска отображает на экране терминала наиболее релевантные рефераты в зависимости от входного запроса, но никак не более четырех рефератов. Тесты:
- программа отображает ноль рефератов
- - один
- - четыре
- - пять (ошибочная ситуация)
5.Если вход или выход программы есть упорядоченное множество (последовательный файл, линейный список, таблицы), то сосредоточить внимание на первом и на последнем элементах одного множества.
Существенное различие между анализом граничных значений и эквивалентным разбиением состоит в том, что анализ граничных значений исследует ситуации, возникающие на и вблизи границ эквивалентных разбиений.
Тестирование модульных программ
Кроме тестирования логики программного модуля, существует проблема тестирования комплексирующих программ, состоящих из нескольких модулей, ибо объединение протестированных модулей не всегда позволяет получать работоспособную комплексирующую программу.
Наиболее распространенным приемом тестирования модульных программ является пошаговое тестирование /1/. На практике применяют две его разновидности:
n восходящее тестирование;
n нисходящее тестирование.
Методика восходящего тестирования включает следующие щаги:
а) сначала тестируются ‘листья’ дерева структуры программы, т.е. модули Е,C,F. Поскольку это вызываемые программы, то для их тестирования программируются драйверы (в некоторых литературных источниках для таких программ применяют термин ‘отлаживающий модуль’) В функции драйвера входит формирование тестовых данных для отлаживаемого модуля и передача ему управления (вызов модуля). Очевидно, что формирование тестовых данных в большинстве случаев
будет ни чем иным, как присвоением конкретных значений входным данным отлаживаемого модуля;
б) затем аналогично тестируются модули вышележащего уровня совместно с уже оттестированными модулями нижележащего уровня. Применительно к рассматриваемому нами примеру проектируются драйверы для тестирования пар B-E и D-F;
в) пошаговый процесс продолжается до тех пор, пока не будет включен в процесс тестирования последний модуль (‘корень’ дерева структуры программы). Для нашего примера это будет модуль А. Для его тестирования совместно с вызываемыми программами нижележащего уровня драйвер разрабатывать не требуется.
Альтернативное нисходящее тестирование состоит из следующих действий:
а) тестирование начинается с вызывающего модуля программы (‘корня’ дерева структуры программы). Для модулей нижележащего уровня (вызываемых) программируются так называемые ‘заглушки’ ( в некоторых литературных источниках эти программы называются ‘имитаторы’. Термин ‘имитатор’ точнее отражает выполняемую программой функцию – работа модуля нижележащего уровня имитируется, т.е без выполнения самого модуля формируются результаты обработки входных данных);
б) после проверки ‘корня’ дерева структуры комплексирующей программы переходят к тестированию нижележащих модулей. Причем, формализованной процедуры подключения к вызывающему модулю нижележащих вызываемых модулей не существует. Единственное ограничение, которым руководствуются при выборе очередного претендента на тестирование, заключается в том, что этот модуль должен вызываться уже оттестированным модулем вышележащего уровня. Если очередной тестируемый модуль вызывает модули еще нижележащего уровня, то аналогично пункту а) для нижележащих модулей программируются ‘заглушки’;
в) процесс тестирования продолжается до тех пор, пока не будет оттестирован последний модуль из ‘листьев’ дерева структуры программы. Приведем две возможные последовательности тестирования модулей согласно этой методики для рассматриваемого примера:
n A (B,C,D), A-B (E), A-B-E, A-C, A-D (F), A-D-F;
n A (B,C,D), A-B (E), A-C, A-D (F), A-B-E, A-D-F.
В скобках указаны имена модулей, для которых должны быть запрограммированы ‘заглушки’. Через черточку указываются композиции тестируемых модулей.
Сравнительный анализ приведенных методик тестирования / /, показывает, что нисходящее тестирование ставит перед программистом много проблем, касающихся разработки ‘заглушек’ и интерпретации результатов тестирования. Там же /1/ приводится сравнительная таблица недостатков и преимуществ методов восходящего и нисходящего тестирования, из которой явствует, что меньшими недостатками обладает восходящее тестирование.
Отладка
Отладкой называется процесс выявления природы ошибки программы и исправления ошибок, после того, как ошибки были обнаружены в процессе тестирования.
Из всех этапов проектирования логики программных модулей этап отладки является наименее формализованным. В нем выделяют две задачи:
n определение природы ошибки;
n исправление ошибки.
Решение первой из этих задач занимает около 95 % времени, затрачиваемых на отладку. Поэтому любые средства ускорения процесса определения местоположения ошибки в программ имеют важное значение.
Наиболее распространенными и наименее эффективными для отладки являются так называемые методы ‘грубой силы’. К ним относят:
n отладку с использованием дампа памяти;
n отладку с использованием операторов печати по всей программе;
n отладку с использованием автоматических средств.
Дампы памяти практически невозможно использовать при программировании на языке высокого уровня, поэтому на этом способе отладки останавливаться не будем
Расстановка печатей в программе бессистемно вряд ли может ускорить отладку. Для системной расстановки нужны четкие представления о структуре алгоритма (схема работы программы или блок-схема).Серьезным недостатком этого метода отладки является тот факт, что печати, вставляемые в программу, изменяют ее, вследствие чего могут послужить дополнительным источником ошибок.
Использование автоматических средств отладки из этой группы методов наиболее предпочтительно.
Общей характеристикой методов ‘грубой силы’ является то, что они не требуют значительных умственных затрат и могут продолжаться бесконечно долго, если наряду с ними не применять более гибкие методы, к которым относятся:
n метод индукции;
n метод дедукции.
Название методов напоминают о криминалистике и не напрасно, ибо есть аналогия между этими методами и расследованием преступления.
Метод индукции включает:
1) определение данных тестирования, имеющих отношение к ошибке;
2) анализ от частного к общему позволит выявить закономерности в данных пункта 1);
3) в результате анализа (п.2) выдвигается гипотеза о причине ошибки;
4) для подтверждения гипотезы 3 разрабатывается один или больше тестов, которые должны либо подтвердить, либо опровергнуть гипотезу;
5) если дополнительные тесты подтверждают гипотезу, можно приступать к исправлению ошибки, а вот если не подтверждают, то требуется в лучшем случае возврат к п.3, а в худшем – к п.2.
Альтернативный метод дедукции заключается в:
1) перечисление возможных причин или гипотез:
2) использование данных тестирования для исключения некоторых возможных причин;
3) уточнение выбранной наиболее вероятной гипотезы, возможно с использованием дополнительных тестов:
4) доказательство выбранной гипотезы совпадает с п.4 и п.5 метода индукции.
Ниже приводятся рекомендации по организации отладки в форме заповедей
Заповедь 1. Считайте тестирование ключевой задачей разработки ПС, поручайте его самым квалифицированным и одаренным программистам; нежелательно тестировать свою собственную программу.
Заповедь 2. Хорош тот тест, для которого высока вероятность обнаружить ошибку, а не тот, который демонстрирует правильную работу программы.
Заповедь 3. Готовьте тесты как для правильных, так и для неправильных данных.
Заповедь 4. Избегайте невоспроизводимых тестов, документируйте их пропуск через компьютер; детально изучайте результаты каждого теста.
Заповедь 5. Каждый модуль подключайте к программе только один раз; никогда не изменяйте программу, чтобы облегчить ее тестирование.
Заповедь 6. Пропускайте заново все тесты, связанные с проверкой работы какой-либо программы ПС или ее взаимодействия с другими программами, если в нее были внесены изменения (например, в результате устранения ошибки).
Похожие записи
No user прокомментировали сообщение
Оставить комментарий