Устойчивость — свойство осуществлять требуемое преобразование информации при сохранении выходных решений программы в пределах допусков, установленных спецификацией. Устойчивость характеризует поведение программы при воздействии на нее таких факторов неустойчивости, как ошибки операторов ЭВМ, а также не выявленные ошибки программы.
Восстанавливаемость — свойство программного обеспечения, характеризующее возможность приспосабливаться к обнаружению ошибок и их устранению.
Надежность можно представить совокупностью следующих характеристик:
— целостностью программного средства (способностью его к защите от отказов);
— живучестью (способностью к входному контролю данных и их проверки в ходе работы);
— завершенностью (бездефектностью готового программного средства, характеристикой качества его тестирования);
— работоспособностью (способностью программного средства к восстановлению своих возможностей после сбоев).
Отличие понятия корректности и надежности программ состоит в следующем:
— надежность характеризует как программу, так и ее "окружение" (качество аппаратуры, квалификацию пользователя и т. п.);
— говоря о надежности программы, обычно допускают определенную, хотя и малую долю ошибок в ней и оценивают вероятность их появления.
Вернемся к понятию корректности. Очевидно, что не всякая синтаксически правильная программа является корректной в указанном выше смысле, т. е. корректность характеризует семантические свойства программ.
С учетом специфики появления ошибок в программах можно выделить две стороны понятия корректности:
— корректность как точное соответствие целям разработки программы (которые отражены в спецификации) при условии ее завершения или частичная корректность;
— завершение программы, т. е. достижение программой в процессе ее выполнения своей конечной точки.
В зависимости от выполнения или невыполнения каждого из двух названных свойств программы различают шесть задач анализа корректности:
1) доказательство частичной корректности;
2) доказательство частичной некорректности;
3) доказательство завершения программы;
4) доказательство не завершения программы;
5) доказательство тотальной (полной) корректности (т. е. одновременное решение 1-й и 3-й задач);
6) доказательство некорректности (решение 2-й или 4-й задачи). Методы доказательства частичной корректности программ, как
правило, опираются на аксиоматический подход к формализации семантики языков программирования. Аксиоматическая семантика языка программирования представляет собой совокупность аксиом и правил вывода. С помощью аксиом задается семантика простых операторов языка (присваивания, ввода-вывода, вызова процедур). С помощью правил вывода описывается семантика составных операторов или управляющих структур (последовательности, условного выбора, циклов). Среди этих правил вывода надо отметить правило вывода для операторов цикла, так как оно требует знания инварианта цикла (формулы, истинность которой не изменяется при любом прохождении цикла).
Наиболее известным из методов доказательства частичной корректности программ является метод индуктивных утверждений, предложенный Флойдом и усовершенствованный Хоаром. Один из важных этапов этого метода — получение аннотированной программы. На этом этапе для синтаксически правильной программы должны быть заданы утверждения на языке логики предикатов первого порядка: входной предикат; выходной предикат.
Эти утверждения задаются для входной точки цикла и должны характеризовать семантику вычислений в цикле.
Доказательство неистинности условий корректности свидетельствует о неправильности программы или ее спецификации, или программы и спецификации.
По влиянию на результаты обработки информации к надежности и устойчивости программного обеспечения близка и точность программного обеспечения, определяемая ошибками метода и ошибками представления данных.
Наиболее простыми методами оценки надежности программного обеспечения являются эмпирические модели, основанные на опыте разработки программ: если до начала тестирования на 1000 операторов приходится 10 ошибок, а приемлемым качеством является 1 ошибка, то в ходе тестирования надо найти:
Более точна модель Холстеда: N ошибок = N операторов * log2(N операторов — N операндов),
где N операторов — число операторов в программе; N операндов — число операндов в программе.
Эмпирическая модель фирмы IBM:
N ошибок = 23 M(10) + 2 M(1),
где M(10) — число модулей с 10 и более исправлениями; M(1) — число модулей с менее 10 исправлениями.
Если в модуле обнаружено более 10 ошибок, то его программируют заново.
По методу Милса в разрабатываемую программу вносят заранее известное число ошибок. Далее считают, что темпы выявления ошибок (известных и неизвестных) одинаковы.
11.3. СВЯЗЬ ПРОЦЕССОВ ТЕСТИРОВАНИЯ С ПРОЦЕССОМ ПРОЕКТИРОВАНИЯ
Из рис. 11.2 видно, что ошибки на ранних этапах проекта исчерпывающе могут быть выявлены в самом конце работы.
Тестирование программ охватывает ряд видов деятельности:
• постановку задачи;
• проектирование тестов;
• написание тестов;
• тестирование тестов;
• выполнение тестов;
• изучение результатов тестирования.
Здесь наиболее важным является проектирование тестов.
Итак, тестирование — это процесс выполнения программы с целью обнаружения ошибок.
11.4. ПОДХОДЫ К ПРОЕКТИРОВАНИЮ ТЕСТОВ
Рассмотрим два самых противоположных подхода к проектированию тестов.
Сторонник первого подхода ориентируется только на стратегию тестирования, называемую стратегией "черного ящика", тестированием с управлением по данным или тестированием с управлением по входу-выходу. При использовании этой стратегии программа рассматривается как черный ящик. Тестовые данные используются только в соответствии со спецификацией программы (т. е. без учета знаний о ее внутренней структуре). Недостижимый идеал сторонника первого подхода — проверить все возможные комбинации и значения на входе. Обычно их слишком много даже для простейших алгоритмов. Так, для программы расчета среднего арифметического четырех чисел надо готовить 107 тестовых данных.
Рис. 11.2. Взаимосвязь процессов проектирования и тестирования
При первом подходе обнаружение всех ошибок в программе является критерием исчерпывающего входного тестирования. Последнее может быть достигнуто, если в качестве тестовых наборов использовать все возможные наборы входных данных. Следовательно, приходим к выводу, что для исчерпывающего тестирования программы требуется бесконечное число тестов, а значит, построение исчерпывающего входного теста невозможно. Это подтверждается двумя аргументами: во-первых, нельзя создать тест, гарантирующий отсутствие ошибок; во-вторых, разработка таких тестов противоречит экономическим требованиям. Поскольку исчерпывающее тестирование исключается, нашей целью должна стать максимизация результативности капиталовложений в тестирование (максимизация числа ошибок, обнаруживаемых одним тестом). Для этого необходимо рассматривать внутреннюю структуру программы и делать некоторые разумные, но, конечно, не обладающие полной гарантией достоверности предположения.
Сторонник второго подхода использует стратегию "белого ящика", или стратегию тестирования, управляемую логикой программы, которая позволяет исследовать внутреннюю структуру программы. В этом случае тестировщик получает тестовые данные путем анализа только логики программы; стремится, чтобы каждая команда была выполнена хотя бы один раз. При достаточной квалификации добивается, чтобы каждая команда условного перехода выполнялась бы в каждом направлении хотя бы один раз. Цикл должен выполняться один раз, ни разу, максимальное число раз. Цель тестирования всех путей извне также недостижима. В программе из двух последовательных циклов внутри каждого из них включено ветвление на десять путей, имеется 1018 путей расчета. Причем выполнение всех путей расчета не гарантирует выполнения всех спецификаций. Для справки: возраст Вселенной 1017с.