Лекция № 10
Тестирование и отладка программ
Тестирование – выполнение программы с целью обнаружения ошибок.
Дейкстра: "Никакое тестирование не может подтвердить правильность программы: в лучшем случае, оно может показать только ее ошибочность".
Отладка – локализация и исправление ошибок.
1. Синтаксические Статический контроль и диагностика компилятором и компоновщиком
2. Ошибки выполнения, выявляемые Динамический контроль:
автоматически:
а) переполнение, потеря порядка,... - аппаратурой процессора (Вопрос 1)
б) несоответствие типов - run-time системы программирования
в) зацикливание - операционной системой – по превы-
шению лимита времени задачи
3. Программа не соответствует специ- Целенаправленное тестирование
фикации
4. Спецификация не соответствует Испытания, бета-тестирование
требованиям – ошибка спецификации
Глубина контроля 1-го вида зависит и от языка, и от компилятора. Строгая типизация весьма полезна: DO 3 I = 1.3 – “точка стоимостью 800 млн $” (вместо запятой) – ошибка в Фортран-программе бортового вычислителя ракеты к Венере [1]. Вопрос 2.
Набор ошибок 2-го вида может быть расширен программистом: контроль можно программировать с помощью утверждений (asserts) проверок, вставляемых в код. Это полезно для проверки правдоподобности промежуточных результатов вычислений и допустимости значений фактических параметров подпрограмм.
Собственно процесс тестирования направлен на выявление ошибок 3 и 4 видов. Большинство программистов сами исправляют 99% своих текущих ошибок. Однако порядка одной ошибки на 100 строк кода обычно еще остается, когда программист сдает работу тестеру, утверждая, что ошибок в ней нет [2].
Тест – это набор контрольных входных данных совместно с ожидаемыми результатами. К входным данным здесь относятся не только конкретные значения ввода, но и события, их последовательность и временные параметры. Ожидаемые результаты берутся из спецификации программы, а на этапе приемо-сдаточных испытаний это – ожидания пользователей.
Ключевой вопрос – полнота тестирования: какое количество каких тестов гарантирует возможно более полную проверку программы? Исчерпывающая проверка на всем множестве входных данных недостижима. Пример: программа, вычисляющая функцию двух переменных: Y = f (X, Z). Если X, Y, Z – real, то полное число тестов (232)2 = 264 ≈ 1031. Если на каждый тест тратить 1 мс, то 1031 мс = 800 млн лет (отсюда видно, что ошибка FDIV Pentium’а вполне простительна). Все траектории выполнения кода также невозможно воспроизвести. В [1] приведена программа из двадцати строк кода (цикл и несколько операторов IF), у которой 1017 возможных путей выполнения.
Следовательно:
· В любой нетривиальной программе на любой стадии ее готовности содержатся необнаруженные ошибки
· Продолжительность тестирования – технико-экономическая проблема: компромисс между временем и полнотой. Поэтому нужно возможно меньшее количество хороших тестов с желательными свойствами:
Ø Детективность: тест должен с большой вероятностью обнаруживать возможные ошибки.
Ø Покрывающая способность: один тест должен выявлять как можно больше ошибок.
Ø Воспроизводимость: ошибка должна выявляться независимо от изменяющихся условий (например, от временных соотношений) – это трудно достижимо для время-зависимых программ, реультаты которых часто невоспроизводимы.
Это благие пожелания; для направленного выбора руководствуются критериями выбора тестов. Критерий должен показать, когда некоторое конечное множество тестов достаточно для проверки программы с некоторой полнотой.
Два вида критериев:
§ Функциональные – если тесты составляются исходя из спецификации программы (тестирование черного ящика). Проверяется правильность выполнения программой всех ее заданных функций. Именно этим критериям в основном и следуют при независимом тестировании.
§ Структурные – если тесты составляются исходя из текста программы (тестирование прозрачного ящика). Проверяется правильность работы при прохождении всех участков кода. Эту работу программисты выполняют постоянно в ходе разработки.