Технологии программирования

Камаев В А

Костерин В В

Глава 11

ТЕСТИРОВАНИЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

 

 

11.1. ОСНОВНЫЕ СВЕДЕНИЯ

Любой заказчик хочет получить надежное программное изделие, которое полностью удовлетворяет его потребности. Различные уровни надежности обеспечиваются разными инженерными подходами к тестированию. Другими словами, за достаточные средства можно достичь уровня надежности как у такой-то фирмы или даже уровня, необходимого для атомной энергетики или космических исследований.

Итак, уровень надежности программных изделий определяется инженерией тестирования. Как достичь высочайшего уровня надежности? Ясно, что тексты программ, написанные в одной организации, можно заново инспектировать, тестировать автономно и в составе ядра в иной организации. В самой программе можно одновременно производить расчеты по разным алгоритмам с использованием разных вычислительных методов и сличать результаты в контрольных точках, использовать подстановки рассчитанных результатов в исходные уравнения и тем самым контролировать результаты решения. Из изложенного видно, что уровень надежности программных изделий напрямую связан с затратами как денежных средств, так и времени проекта.

Как известно, при создании типичного программного проекта около 50 % общего времени и более 50–60 % общей стоимости расходуется на проверку (тестирование) разрабатываемой программы или системы. Кроме того, доля стоимости тестирования в общей стоимости программ имеет тенденцию возрастать при увеличении сложности программных изделий и повышении требований к их качеству.

Учитывая это, при выборе способа тестирования программ следует четко выделять определенное (по возможности не очень большое) число правил отладки, обеспечивающих высокое качество программного продукта и снижающих затраты на его создание.

Тестирование осуществляется путем исполнения тестов. Смысл теста программ показан на рис. 11.1.

Аксиомы тестирования, выдвинутые ведущими программистами:

— хорош тот тест, для которого высока вероятность обнаружения ошибки;

— главная проблема тестирования — решить, когда закончить (обычно решается просто — кончаются деньги);

— невозможно тестировать свою собственную программу;

— необходимая часть тестов — описание выходных результатов;

— избегайте невоспроизводимых тестов;

— готовьте тесты как для правильных, так и для неправильных данных;

— не тестируйте "с лету";

— детально изучайте результаты каждого теста;

— по мере обнаружения все большего числа ошибок в некотором модуле или программе, растет вероятность обнаружения в ней еще большего числа ошибок;

— тестируют программы лучшие умы;

— считают тестируемость главной задачей разработчиков программы;

— не изменяй программу, чтобы облегчить тестирование;

— тестирование должно начинаться с постановки целей.

Если в программе ставят комментарии на месте вызова модуля вместо использования заглушки, то исключают возможность проверки типов данных, а также часто забывают снимать комментарии. Для поиска "забытых" комментариев необходима трудоемкая отладка.

Среди приемов тестирования стоит выделить также так называемую отладочную печать. Если отладочные печати изымаются из текста, то утяжеляется сопровождение. Вывод: никогда не изымай отладочные печати даже с использованием препроцессора:

{$IFDEF DEBUG THEN}

{$ELSE}

{$ENDIF}

Рис. 11.1. Смысл теста программ

Лучше опишите глобальную переменную DebugLevel и программируйте условные отладочные печати:

var

DebugLevel: word; {0-нет ни одной отладочной печати, чем больше значение, тем подробнее отладочная печать}

DebugFile: text; {файл отладочной печати}

DebugLevel:= 4; {задание уровня отладки}

if DebugLevel >= 3 then

WriteLn(DebugFile, 'Модуль:', 'MyModule, 'результат:');

 

11.2. СВОЙСТВА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

Следует выделить следующие свойства программного обеспечения.

Корректность программного обеспечения — свойство безошибочной реализации требуемого алгоритма при отсутствии таких мешающих факторов, как ошибки входных данных, ошибки операторов ЭВМ (людей), сбои и отказы ЭВМ.

В интуитивном смысле под корректностью понимают свойства программы, свидетельствующие об отсутствии в ней ошибок, допущенных разработчиком на различных этапах проектирования (спецификации, проектирования алгоритма и структур данных, кодировании). Корректность самой программы понимают по отношению к целям, поставленным перед ее разработкой (т. е. это относительное свойство).

Устойчивость — свойство осуществлять требуемое преобразование информации при сохранении выходных решений программы в пределах допусков, установленных спецификацией. Устойчивость характеризует поведение программы при воздействии на нее таких факторов неустойчивости, как ошибки операторов ЭВМ, а также не выявленные ошибки программы.

Восстанавливаемость — свойство программного обеспечения, характеризующее возможность приспосабливаться к обнаружению ошибок и их устранению.

Надежность можно представить совокупностью следующих характеристик:

— целостностью программного средства (способностью его к защите от отказов);

— живучестью (способностью к входному контролю данных и их проверки в ходе работы);

— завершенностью (бездефектностью готового программного средства, характеристикой качества его тестирования);

— работоспособностью (способностью программного средства к восстановлению своих возможностей после сбоев).

Отличие понятия корректности и надежности программ состоит в следующем:

— надежность характеризует как программу, так и ее "окружение" (качество аппаратуры, квалификацию пользователя и т. п.);

— говоря о надежности программы, обычно допускают определенную, хотя и малую долю ошибок в ней и оценивают вероятность их появления.

Вернемся к понятию корректности. Очевидно, что не всякая синтаксически правильная программа является корректной в указанном выше смысле, т. е. корректность характеризует семантические свойства программ.

С учетом специфики появления ошибок в программах можно выделить две стороны понятия корректности:

— корректность как точное соответствие целям разработки программы (которые отражены в спецификации) при условии ее завершения или частичная корректность;

— завершение программы, т. е. достижение программой в процессе ее выполнения своей конечной точки.

В зависимости от выполнения или невыполнения каждого из двух названных свойств программы различают шесть задач анализа корректности:

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с.

Сравним способ построения тестов при данной стратегии с исчерпывающим входным тестированием стратегии "черного ящика". Неверно предположение, что достаточно построить такой набор тестов, в котором каждый оператор исполняется хотя бы один раз. Исчерпывающему входному тестированию может быть поставлено в соответствие исчерпывающее тестирование маршрутов. Подразумевается, что программа проверена полностью, если с помощью тестов удается осуществить выполнение этой программы по всем возможным маршрутам ее потока (графа) передач управления.

Последнее утверждение имеет два слабых пункта: во-первых, число не повторяющих друг друга маршрутов — астрономическое; во-вторых, даже если каждый маршрут может быть проверен, сама программа может содержать ошибки (например, некоторые маршруты пропущены).

Свойство пути выполняться правильно для одних данных и неправильно для других — называемое чувствительностью к данным, наиболее часто проявляется за счет численных погрешностей и погрешностей усечения методов. Тестирование каждого из всех маршрутов одним тестом не гарантирует выявление чувствительности к данным.

В результате всех изложенных выше замечаний отметим, что ни исчерпывающее входное тестирование, ни исчерпывающее тестирование маршрутов не могут стать полезными стратегиями, потому что оба они нереализуемы. Поэтому реальным путем, который позволит создать хорошую, но, конечно, не абсолютную стратегию, является сочетание тестирования программы несколькими методами.

Рассмотрим пример тестирования оператора

if A and В then…

при использовании разных критериев полноты тестирования.

При критерии покрытия условий требовались бы два теста: А = true, В = false и А = false, В = true. Но в этом случае не выполняется then-предложение оператора if.

Существует еще один критерий, названный покрытием решений/условий. Он требует такого достаточного набора тестов, чтобы все возможные результаты каждого условия в решении выполнялись, по крайней мере, один раз; все результаты каждого решения выполнялись тоже один раз и каждой точке входа передавалось управление, по крайней мере, один раз.

Недостатком критерия покрытия решений/условий является невозможность его применения для выполнения всех результатов всех условий. Часто подобное выполнение имеет место вследствие того, что определенные условия скрыты другими условиями. Например, если условие AND есть ложь, то никакое из последующих условий в выражении не будет выполнено. Аналогично, если условие OR есть истина, то никакое из последующих условий не будет выполнено. Следовательно, критерии покрытия условий и покрытия решений/условий недостаточно чувствительны к ошибкам в логических выражениях.

Критерием, который решает эти и некоторые другие проблемы, является комбинаторное покрытие условий. Он требует создания такого числа тестов, чтобы все возможные комбинации результатов условия в каждом решении и все точки входа выполнялись, по крайней мере, один раз.

В случае циклов число тестов для удовлетворения критерию комбинаторного покрытия условий обычно больше, чем число путей.

Легко видеть, что набор тестов, удовлетворяющий критерию комбинаторного покрытия условий, удовлетворяет также и критериям покрытия решений, покрытия условий и покрытия решений/условий.

Таким образом, для программ, содержащих только одно условие на каждое решение, минимальным является критерий, набор тестов которого вызывает выполнение всех результатов каждого решения, по крайней мере, один раз; передает управление каждой точке входа (например, оператор CASE).

Для программ, содержащих решения, каждое из которых имеет более одного условия, минимальный критерий состоит из набора тестов, вызывающих все возможные комбинации результатов условий в каждом решении и передающих управление каждой точке входа программы, по крайней мере, один раз.

Деление алгоритма на типовые стандартные структуры, согласно проектной процедуре кодирования текста модуля (метода) из пятой главы учебника, позволяет минимизировать усилия программиста, затрачиваемые им на тестирование. Запрет на вложенные структуры как раз и объясняется излишними затратами на тестирование. Использование цепочки простых альтернатив с одним действием или структуры ВЫБОР вместо вложенных простых АЛЬТЕРНАТИВ значительно сокращает число тестов!

 

11.5. ПРОЕКТИРОВАНИЕ ТЕСТОВ БОЛЬШИХ ПРОГРАММ

Проектирование тестов больших программ пока в большей мере остается искусством и в меньшей мере является наукой. Чтобы построить разумную стратегию тестирования, надо разумно сочетать оба этих два крайних подхода и пользоваться математическими доказательствами.

Восходящее тестирование. Сначала автономно тестируются модули нижних уровней, которые не вызывают других модулей. При этом достигается такая же их высокая надежность, как и у встроенных в компилятор функций. Затем тестируются модули более высоких уровней вместе с уже проверенными модулями и т. д. по схеме иерархии.

При восходящем тестировании для каждого модуля необходима ведущая программа. Это монитор или драйвер, который подает тесты в соответствии со спецификациями тестов. Ряд фирм выпускает промышленные драйверы или мониторы тестов.

Нисходящее тестирование. При этом подходе изолированно тестируется головной модуль или группа модулей головного ядра. Программа собирается и тестируется сверху вниз. Недостающие модули заменяются заглушками.

Достоинства нисходящего тестирования: этот метод совмещает тестирование модуля с тестированием сопряжений и частично тестирует функции модуля. Когда уже начинает работать ввод/вывод модуля, удобно готовить тесты.

Недостатки нисходящего тестирования: модуль редко досконально тестируется сразу после его подключения. Для основательного тестирования требуются изощренные заглушки. Часто программисты откладывают тщательное тестирование и даже забывают о нем. Другой недостаток — желание начать программирование еще до конца проектирования. Если ядро уже запрограммировано, то возникает сопротивление всяким его изменениям, даже для улучшения структуры программы. В конечном итоге, именно рационализация структуры программы за счет проведения проектных итераций способствует достижению большей экономии, чем даст раннее программирование.

Модифицированный нисходящий метод. Согласно этому методу, каждый модуль автономно тестируется перед включением в программу, собираемую сверху вниз.

Метод большого скачка — каждый модуль тестируется автономно. По окончании автономного тестирования всех модулей модули просто интегрируются в готовую программную систему. Как правило, этот метод нежелателен. Однако если программа мала и хорошо спроектирована по сопряжениям, то метод большого скачка вполне приемлем.

Метод сандвича представляет собой компромисс между нисходящим и восходящим подходами. По этому методу реализация и тестирование ведутся одновременно сверху и снизу, и два этих процесса встречаются в заранее намеченной временной точке.

Модифицированный метод сандвича: нижние модули тестируются строго снизу вверх, а модули верхних модулей сначала тестируются автономно, а затем собираются нисходящим методом.

 

11.6. КРИТЕРИИ ВЫБОРА НАИЛУЧШЕЙ СТРАТЕГИИ РЕАЛИЗАЦИИ

Критериями выбора наилучшей стратегии реализации программы являются:

• время до полной сборки программы;

• время реализации скелета программы;

• имеющийся инструментарий тестирования;

• мера параллелизма ранних этапов реализации;

• возможность проверки любых путей программы данными из заглушек;

• сложность планирования и соблюдения графика реализации;

• сложность тестирования.

 

11.7. СПОСОБЫ И ВИДЫ ТЕСТИРОВАНИЯ ПОДПРОГРАММ. ПРОЕКТИРОВАНИЕ ТЕСТОВ

Существуют два способа тестирования: публичный и приватный.

Публичное тестирование означает, что любой желающий может получить данный продукт (либо бесплатно, либо по цене дисков и доставки).

Приватный способ тестирования означает, что проверку продукта производит ограниченный круг тестировщиков, которые выразили согласие активно работать с продуктом и оперативно сообщать обо всех выявленных дефектах. Часто используются оба способа: сначала программа проходит приватное тестирование, а затем, когда все крупные проколы уже выявлены, начинается ее публичное тестирование, чтобы собрать отзывы широкого круга пользователей.

Большинство компаний-разработчиков требуют, чтобы приватный бета-тестировщик подписал "Соглашение о неразглашении" (NDA, Non-Disclosure Agreement). Тем самым он обязуется не разглашать подробности о тестируемом продукте и не передавать его копии третьим лицам. Подобные соглашения подписываются в целях сохранения коммерческой тайны и во избежание недобросовестной конкуренции.

Так, фирма "Microsoft" использует все перечисленные выше подходы для тестирования своих продуктов. С ее сайтов всегда можно бесплатно загрузить большое количество продуктов, проходящих публичное бета-тестирование. Однако их появлению предшествует многомесячный процесс приватного тестирования. Фирма "Microsoft" проводит политику открытого набора приватных бета-тестировщиков, при которой каждый желающий может сообщить корпорации о своем желании принять участие в тестировании того или иного продукта. На самом деле круг приватных бета-тестеров Microsoft очень узок, и шанс, что вас включат в их число, невелик. Тем не менее он существует, а попадают в список бета-тестеров практически пожизненно.

Приватное тестирование подпрограмм начинается с этапа контроля, основными разновидностями которого являются: визуальный, статический и динамический.

Визуальный контроль — это проверка текстов "за столом", без использования компьютера.

На первом этапе визуального контроля осуществляется чтение текста подпрограммы, причем особое внимание уделяется: комментариям и их соответствию тексту программы; условиям в операторах условного выбора и цикла; сложным логическим выражениям; возможности незавершения итерационных циклов.

Второй этап визуального контроля — сквозной контроль текста подпрограммы (его ручная прокрутка на нескольких заранее подобранных простых тестах). Распространенное мнение, что более выгодным является перекладывание большей части работы по контролю программных средств на компьютер, ошибочно. Основной довод в пользу этого таков: при работе на компьютере главным образом совершенствуются навыки в использовании клавиатуры, в то время как программистская квалификация приобретается, прежде всего, за столом.

Статический контроль — это проверка текста подпрограммы (без выполнения) с помощью инструментальных средств.

Первой, наиболее известной формой статического контроля является синтаксический контроль программы с помощью компилятора, при котором проверяется соответствие текста программы синтаксическим правилам языка программирования.

Сообщения компилятора обычно делятся на несколько групп в зависимости от уровня тяжести нарушения синтаксиса языка программирования:

1) информационные сообщения и предупреждения, при обнаружении которых компилятор, как правило, строит корректный объектный код и дальнейшая работа с программой (компоновка, выполнение) возможна (тем не менее сообщения этой группы должны тщательно анализироваться, так как их появление также может свидетельствовать об ошибке в программе — например, из-за неверного понимания синтаксиса языка);

2) сообщения об ошибках, при обнаружении которых компилятор пытается их исправить и строит объектный код, но его корректность маловероятна и дальнейшая работа с ним, скорее всего, невозможна;

3) сообщения о серьезных ошибках, при наличии которых построенный компилятором объектный код заведомо некорректен и его дальнейшее использование невозможно;

4) сообщения об ошибках, обнаружение которых привело к прекращению синтаксического контроля и построения объектного кода.

Однако практически любой компилятор пропускает некоторые виды синтаксических ошибок. Место обнаружения ошибки может находиться далеко по тексту подпрограммы от места истинной ошибки, а текст сообщения компилятора может не указывать на истинную причину ошибки. Одна синтаксическая ошибка может повлечь за собой генерацию компилятором нескольких сообщений об ошибках (например, ошибка в описании переменной приводит к появлению сообщения об ошибке в каждом операторе подпрограммы, использующем эту переменную).

Второй формой статического контроля может быть контроль структурированности текста подпрограммы, т. е. проверка выполнения соглашений и ограничений структурного программирования. Примером подобной проверки может быть выявление в тексте подпрограммы ситуаций, когда цикл образуется с помощью оператора безусловного перехода (использования оператора GOTO для перехода вверх по тексту программы). Для проведения контроля структурированности могут быть созданы специальные инструментальные средства, а при их отсутствии эта форма статического контроля может совмещаться с визуальным контролем.

Третья форма статического контроля — контроль правдоподобия подпрограммы, т. е. выявление в ее тексте конструкций, которые хотя и синтаксически корректны, но скорее всего содержат ошибку или свидетельствуют о ней. Основные неправдоподобные ситуации:

• использование в программе неинициализированных переменных (т. е. переменных, не получивших начального значения);

• наличие в программе описаний элементов, переменных, процедур, меток, файлов, в дальнейшем не используемых в ее тексте;

• наличие в тексте подпрограммы фрагментов, никогда не выполняющихся;

• наличие в тексте программы переменных, ни разу не используемых для чтения после присваивания им значений;

• наличие в тексте подпрограммы заведомо бесконечных циклов.

Даже если присутствие в тексте программы неправдоподобных конструкций не приводит к ее неправильной работе, исправление этого фрагмента повысит ясность и эффективность программы, т. е. благотворно скажется на ее качестве.

Для возможности проведения контроля правдоподобия в полном объеме также должны быть созданы специальные инструментальные средства, хотя ряд возможностей по контролю правдоподобия имеется в существующих отладочных и обычных компиляторах.

Следует отметить, что создание инструментальных средств контроля структурированности и правдоподобия программ может быть упрощено существенно при применении следующих принципов:

1) проведение дополнительных форм статического контроля после завершения компиляции и только для синтаксически корректных программ;

2) максимальное использование результатов компиляции и линковки программы и, в частности, информации, включаемой в листинг компилятора и линкера;

3) вместо полного синтаксического разбора текста проверяемой программы необходимо построение для нее списка идентификаторов и списка операторов с указанием всех их необходимых признаков.

При отсутствии инструментальных средств контроля правдоподобия эта фаза статического контроля также может объединяться с визуальным контролем.

Четвертой формой статического контроля программ является их верификация, т. е. аналитическое доказательство их корректности.

Несмотря на достаточную сложность процесса верификации программы и на то, что даже успешно завершенная верификация не дает гарантий качества программы (так как ошибка может содержаться и в верификации), применение методов аналитического доказательства правильности очень полезно для уточнения смысла разрабатываемой программы, а знание этих методов благотворно сказывается на квалификации программиста.

Динамический контроль программы — это проверка правильности программы при ее выполнении на компьютере, т. е. тестирование. Минимальное автономное тестирование подпрограммы должно обеспечивать прохождение всех путей вычислений.

Проектная процедура тестирования подпрограммы заключается в следующем:

— по внешним спецификациям модуля подготовьте тесты для каждой ситуации и каждого недопустимого условия;

— просмотрите текст подпрограммы, чтобы убедиться, что все условные переходы будут выполняться в каждом направлении; при необходимости добавьте тесты;

— убедитесь по тексту подпрограммы, что тесты охватывают достаточно много путей; для циклов должны быть тесты без повторения, с одним повторением и с несколькими повторениями;

— проверьте по тексту подпрограммы ее чувствительность к особым значениям данных (наиболее опасные числа — это ноль и единица), в случае необходимости добавьте тесты.

 

11.8. ПРОЕКТИРОВАНИЕ КОМПЛЕКСНОГО ТЕСТА

В комплексном тесте должны проводиться следующие виды тестирования:

• работоспособности;

• стрессов;

• предельного объема вводимых данных;

• конфигурации различных технических средств;

• совместимости;

• защиты;

• требуемой памяти;

• производительности;

• настройки;

• надежности;

• средств восстановления при отказе;

• удобства обслуживания;

• программной документации;

• психологических факторов;

• удобства эксплуатации.

 

11.9. СРЕДСТВА АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ

Генераторы тестов (automatic unit test) случайным образом генерируют данные.

Статические анализаторы программ, анализируют исходный тест и строят диаграммы маршрутов; анализируют присваивание данных и делают попытки построений данных, приводящих к ошибке.

Средства периода выполнения обычно производят статистический подсчет количества выполнения каждого оператора и позволяют контролировать полноту тестов.

ВЫВОДЫ

• Тестирование программ — главное, что определяет важнейшее качество программ — надежность.

• На тестирование расходуется основная часть средств и времени проекта.

• При разработке любой программы или системы тестирование отбирает большую часть времени и денег. Учитывая это, необходимо определить не очень большое количество тестов, обеспечивающих высокую вероятность обнаружения тех или иных ошибок.

• Аксиомы тестирования определяют основные цели и принципы тестирования.

• Если из текста исключить отладочные печати, то существенно усложнится сопровождение.

• Существуют два крайних подхода к проектированию тестов: стратегия "черного ящика" и стратегия "белого ящика". Бесполезно следовать только одному подходу. Необходимо строить стратегию тестирования только на основе сочетания подходов.

• При проектировании многомодульных программ используется восходящее тестирование (автономное тестирование нижних модулей, не вызывающих других модулей) и нисходящее тестирование (применение заглушек нижних уровней). Но у каждого из них есть свои достоинства и недостатки. Возможны также варианты:

— модифицированный нисходящий метод — согласно этому методу каждый модуль автономно тестируется перед включением в программу, собираемую сверху вниз;

— метод большого скачка — каждый модуль тестируется автономно, далее модули просто интегрируются в готовую программную систему;

— метод сандвича — по этому методу реализация и тестирование ведутся одновременно сверху и снизу, и два этих процесса встречаются в заранее намеченной временной точке;

— модифицированный метод сандвича — нижние модули тестируются строго снизу вверх, а модули верхних модулей сначала тестируются автономно, а затем собираются нисходящим методом. Этот метод апробирован при создании ОС.

• Существует деление тестирования на публичное и приватное. Часто используются оба способа: сначала программа проходит приватное тестирование, а затем, когда все крупные "проколы" уже выявлены, начинается ее публичное тестирование, чтобы собрать отзывы широкого круга пользователей.

Контрольные вопросы

1. Назовите основные аксиомы тестирования.

2. В чем преимущество отладочной печати?

3. Какие свойства программного обеспечения оказывают наибольшее влияние на процесс обнаружения ошибок при тестировании?

4. Для чего нужны эмпирические модели? Как производится их анализ?

5. Какова связь между процессами тестирования и проектирования?

6. В чем достоинства и недостатки восходящего и нисходящего проектирования? Ответ обосновать на примере конкретной программы.

7. Какой тест максимально быстро обнаружит зацикливание?

8. Выделите несколько основных критериев при выборе параметров тестирования.