Спецификация функций с помощью таблиц решений.

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

На следующем этапе составляются внешние спецификации выявленных функций или, иначе, спецификации процессов. Фактически спецификации являются описаниями алгоритмов соответствующих функций. Для этих целей существует достаточно много средств, которые перечислим в порядке увеличения трудности проектирования алгоритмов /4/:

- текстовое описание,

- структурированный естественный язык,

- таблица решений,

- дерево решений,

- визуальный язык,

- схемы систем, программ, данных и другие нотации для представления алгоритмов (псевдокоды, Flow-формы и диаграммы Насси-Шнейдермана) /10/,

- язык программирования.

Следует отметить, что в перечисленном выше порядке увеличивается степень формализации описания алгоритма и понимание деталей его функционирования проектировщиками и программистами, но уменьшается степень понимания разрабатываемой программы заказчиком и будущим пользователем ПО, для которого она разрабатывается. Компромиссным решением проблемы понимания являются средства специфицирования, лежащие в середине вышеприведенного спектра. Рассмотрим подробнее таблицу решений как вариант этого компромисса /4/.

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

ТР состоит из двух частей. Верхняя часть таблицы используется для определения условий. Обычно условие является ЕСЛИ-частью оператора ЕСЛИ-ТО и требует ответа “да-нет:”. Нижняя часть ТР используется для определения действий, т.е. ТО-части оператора ЕСЛИ-ТО. Левая часть ТР содержит собственно описание условий и действий, а в правой части перечисляются все возможные комбинации условий и, соответственно, указывается, какие конкретно действия и в какой последовательности выполняются, когда определенная комбинация условий имеет место.

Поясним сказанное на примере спецификации функции получения пароля при обслуживании кредитной карты. Очевидно, что при вводе пароля условиями для входных данных являются ситуация нажатия клавиши “ввод”, означающей окончание ввода пароля, и совпадение набранного пароля с комбинацией, записанной на карте. Основными действиями являются проверка правильности пароля и выдача предупреждения при неверном вводе, а побочными действиями – прием очередного символа из комбинации пароля и ожидание нажатия клавиши “ввод”, а по окончании выполнения функции переход к основному меню. Таблица решений для анализируемой функции может выглядеть, как показано на рис. 1.1.

Здесь “Д” означает “да”, “Н” – “нет”, прочерк “-” означает, что условие не проверяется, цифры 1,2 означают, что помеченные действия выполняются в указанном порядке.

Таблица 1.1

Таблица решений для функции “получить пароль”

Условия
Нажата клавиша “ввод”? Н Д Д
Пароль введен верно? - Н Д
Действия      
Ждать ввода    
Выдать предупреждение об ошибке    
Перейти к основному меню  

 

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

Таблица 1.2

Таблица решений для усложненной функции “получить пароль”

Условия
Нажата клавиша “ввод”? Н Д Д Д Д Д Д
Первый ввод пароля? - Д Д Н Н Н Н
Повторный ввод пароля? - Н Н Д Д Д Д
Счетчик повторений равен 3? - Н Н Н Н Д Д
Пароль введен верно? - Д Н Н Д Н Д
Действия              
Ждать ввода            
Выдать предупреждение об ошибке          
Увеличить счетчик повторений          
Блокировать карту клиента            
Окончание таблицы 1.2
Действия
Выдать приглашение к повторному вводу пароля          
Перейти к основному меню      

 

Методика построения ТР заключается в следующем:

а) определить все условия и действия в спецификации;

б) вписать действия и условия в таблицу;

в) в нумерованных столбцах отметить все возможные комбинации условий и порядок выполняемых при этом действий;

г) при необходимости редуцировать таблицу, Редуцирование заключается в следующем: если есть 2 столбца, у которых перечень действий совпадает, и которые отличаются только результатами условий “Д” и “Н” в одной строке, то такие столбцы могут быть слиты в один. В этом столбце отличающиеся исходы “Д” и “Н” заменяются прочерком, что интерпретируется как независимость действий от данного условия. Для ТР 1.2 такими столбцами являются столбцы с номерами 3 и 4. Редуцированная ТР приводится в таблице 1.3.

Таблица 1.3

Редуцированная таблица решений для функции “получить пароль”

Условия
Нажата клавиша “ввод”? Н Д Д Д Д Д
Первый ввод пароля? - Д - Н Н Н
Повторный ввод пароля? - Н Н Д Д Д
Счетчик повторений равен 3? - Н Н Н Д Д
Пароль введен верно? - Д Н Д Н Д
Действия            
Ждать ввода          
Выдать предупреждение об ошибке          
Увеличить счетчик повторений          
Блокировать карту клиента          
Выдать приглашение к повторному вводу пароля          
Перейти к основному меню    

Отметим, что на основе ТР легко осуществить кодирование программы на языке высокого уровня, таком, как Pascal.

 

Стратегии тестирования

 

Тестирование программного обеспечения охватывает целый ряд видов деятельности, аналогичных последовательности процессов разработки программного обеспечения. В него входят /2/:

а) постановка задачи для теста,

б) проектирование теста,

в) написание тестов,

г) тестирование тестов,

д) выполнение тестов,

е) изучение результатов тестирования.

Решающую роль играет проектирование тестов. Возможен целый ряд подходов к стратегии проектирования тестов. Чтобы ориентироваться в них, рассмотрим два крайних подхода /5/. Первый состоит в том, что тесты проектируются на основе внешних спецификаций программ и модулей либо спецификаций сопряжения программы или модуля. Программа при этом рассматривается как черный ящик (стратегия “черного ящика”). Существо такого подхода - проверить, соответствует ли программа внешним спецификациям. При этом логика модуля совершенно не принимается во внимание.

Второй подход основан на анализе логики программы (стратегия “белого ящика”). Существо подхода - в проверке каждого пути, каждой ветви алгоритма. При этом внешняя спецификация во внимание не принимается.

Заметим, что в литературе последних лет издания /6,10/, в отличие от классиков /2,5/, эти стратегии называются соответственно функциональным и структурным подходом.

Ни один из этих подходов не является оптимальным /5/. Из анализа существа первого подхода ясно, что его реализация сводится к проверке всех возможных комбинаций значений на входе программы. Рассмотрим в качестве примера задачу тестирования тривиальной программы, получающей на входе три числа и вычисляющей их среднее арифметическое. Тестирование этой программы для всех значений входных данных невозможно, так как их бесконечное множество. Как правило, исчерпывающее тестирование для всех входных данных программы неосуществимо, поэтому ограничиваются меньшим объемом тестирования. При этом исходят из максимальной отдачи теста по сравнению с затратами на его создание. Она измеряется вероятностью того, что тест выявит ошибки, если они имеются в программе. Затраты измеряются временем и стоимостью подготовки, выполнения и проверки результатов теста.

Проанализируем теперь второй подход к тестированию. На рисунке 2.1 изображены возможные пути небольшого программного модуля /5/.

 

 

Рис.1.2. Блок-схема программного модуля

 

Квадратами представлены последовательные сегменты, а стрелками - передачи управления (с помощью развилок или циклов). Число путей в модуле имеет порядок (для сравнения, возраст вселенной в секундах оценивается как 4´ ). Но даже если предположить, что выполнены тесты для всех путей, можно утверждать, что модуль удовлетворительно не протестирован.

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

IF (A+B+C)/3=A,

то оно будет верным не для всех значений A, B и С (ошибка возникает в том случае, когда из двух значений В или С одно больше, а другое на столько же меньше А). Если концентрировать внимание только на тестировании путей, нет гарантии, что эта ошибка будет выявлена.

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

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

Рекомендуется следующая процедура разработки тестов:
- разрабатывать тесты, используя методы стратегии “черного ящика”;
- дополнительное тестирование, используя методы стратегии “белого ящика”.