ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ РФ
ВОЛОГОДСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
Кафедра автоматики и вычислительной техники
ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
Методические указания
к выполнению лабораторной работы №6 «Методы тестирования»
Факультет электроэнергетический
Специальности:
220201 – информатика и управление в технических системах
230105 – программное обеспечение вычислительной техники и автоматизированных систем
Вологда 2008
УДК 681.3.06
Технология разработки программного обеспечения:
Методические указания к выполнению лабораторной работы №6 «Методы тестирования». - Вологда, ВоГТУ, 2008. - 31 с.
Методические указания содержат описание и методику выполнения лабораторной работы по указанной дисциплине. Для лабораторной работы указывается цель работы, приводится необходимый теоретический материал, методика проведения экспериментов. Имеются также варианты заданий и контрольные вопросы по теме работы.
Утверждено редакционно – издательским советом ВоГТУ.
Составитель: Сергушичева А.П., доц. каф. АТПП.
Рецензент: Водовозов А.М., зав.кафедрой УВС канд.техн. наук, доц.
ЛАБОРАТОРНАЯ РАБОТА №6
Методы тестирования
ВВЕДЕНИЕ
Тестирование – важный и трудоемкий процесс в разработке программного обеспечения. Оно должно выявить подавляющее большинство ошибок, допущенных при составлении программ. Выделяют три стадии тестирования: автономное, комплексное и системное. Основными подходами к формированию тестов являются структурный и функциональный. Каждый из указанных подходов имеет свои особенности и области применения. Лабораторная работа предназначена для формирования у студентов навыков тестирования программных продуктов и направлена на изучение методов тестирования и способов формирования тестовых данных.
В качестве практического задания студентам предлагается сформировать тестовые данные для конкретных программ, а также протестировать предложенные программные продукты различными методами. Для большей наглядности лабораторная работа снабжена примерами выполнения заданий.
За помощь в подготовке данной лабораторной работы хочется выразить благодарность студентке группы ЭПО-51 2008г. выпуска Давыдовой Ольге.
1. ЦЕЛЬ РАБОТЫ
Цель работы: изучение методов тестирования и способов формирования тестовых наборов, приобретение практических навыков по тестированию программных продуктов. Продолжительность работы - 4 часа.
2. ОСНОВНЫЕ ТЕОРЕТИЧЕСКИЕ ПОЛОЖЕНИЯ
2.1. Тестирование. Задачи. Стратегии
Тестирование программных средств (ПС) это процесс выполнения программ или иная деятельность с программой и программными документами с целью обнаружения и исправления ошибок или аттестации ПС. При отладке ПС локализуются и устраняются, в основном, те ошибки, наличие которых в ПС устанавливается при тестировании. Исчерпывающее тестирование, как правило, невозможно, а тестированием ПС практически выполнимым набором тестов нельзя установить наличие всех имеющихся в ПС ошибок, т.е. тестирование не может доказать правильность ПС, в лучшем случае оно может продемонстрировать наличие или отсутствие определенных ошибок в программном обеспечении. Поэтому возникает задача подготовки такого набора тестов, чтобы при применении их к ПС обнаружить по возможности большее число ошибок. Тестом или тестовым набором называется набор данных, для которого заранее известен результат применения или известны правила поведения программ. В соответствии с определением тестирования, удачным следует считать тест, ко¬торый обнаруживает хотя бы одну ошибку.
Другой задачей, которая решается при организации тестирования является определение момента его окончания, т.к. чем дольше продолжается процесс тестирования (и отладки в целом), тем большей становится стоимость ПС. Признаком возможности завершения отладки является полнота охвата тестами, пропущенными через ПС, множества различных ситуаций, возникающих при выполнении программ ПС, и относительно редкое проявление ошибок в ПС на последнем отрезке процесса тестирования. Последнее определяется в соответствии с требуемой степенью надежности ПС, указанной в спецификации его качества.
Проектирование тестов можно начинать сразу после завершения этапа внешнего описания ПС. Выделяют две стратегии проектирования тестов. Первый подход заключается в создании тестов на основании изучения спецификаций ПС (внешнего описания, описания архитектуры и спецификации модулей). Строение модулей при этом не учитывается, т.е. они рассматриваются как черные ящики. Данный подход называют стратегией "черного ящика" (функциональным тестированием, подходом, управляемым данными). При втором подходе тесты проектируются на основании анализа текстов программ с целью охвата ими всех путей выполнения программ ПС (стратегия "белого (прозрачного, стеклян¬ного) ящика" или структурное тестирование). В этом случае проверяют правильность реализации заданной логики в коде программы. Если принять во внимание большое число возможных комбинаций входных данных и наличие в программах циклов с переменным числом повторений (большое число различных путей выполнения программ), то становится очевидной невозможность исчерпывающего тестирования ПС.
Наборы тестов, полученные в соответствии с методами этих подходов, обычно объединяют, обеспечивая всестороннее тестирование программного обеспечения.
2.2. Методы ручного контроля программного обеспечения
Ручной контроль обычно используют на ранних эта¬пах разработки, т.к. воз¬можность практической проверки подобных решений в этот период отсутствует, большое значение имеет их обсуждение, которое прово¬дят в разных формах. Основными методами ручного контроля являются: инспекции исходного текста, сквозные просмотры, проверка за столом, оценка программ.
Инспекции исходного текста представ¬ляют собой набор процедур и приемов обнаружения ошибок при изучении текста группой специалистов. В эту группу входят: автор программы, проек¬тировщик, специалист по тестированию и координатор - компетентный про¬граммист, но не автор программы. Общая процедура инспекции предполага¬ет следующие операции:
• участникам группы заранее выдается листинг программы и спецификация на нее;
• программист рассказывает о логике работы программы и отвечает на
вопросы инспекторов;
• программа анализируется по списку вопросов для выявления исторически сложившихся общих ошибок программирования.
Список вопросов для инспекций исходного текста зависит, как от ис-пользуемого языка программирования, так и от специфики разрабатываемо¬го программного обеспечения. Пример списка во¬просов, который можно использовать при анализе правильности программ, написанных на языке Pascal, приведен в приложении А.
Кроме непосредственного обнаружения ошибок, результаты инспекции позволяют программисту увидеть другие сделанные им ошибки, получить возможность оценить свой стиль программирования, выбор алгоритмов и методов тестирования. Инспекция является способом раннего выявления ча¬стей программы, с большей вероятностью содержащих ошибки, что позволя¬ет при тестировании уделить внимание именно этим частям.
Сквозные просмотры осуществляются группой специалистов при изучении листинга программы и спецификации на нее на группе тестов. Участники заседания мысленно выполняют каждый тест в соответствии с логикой программы. При этом состояние программы (значения переменных) отслеживается на бумаге (доске).
Проверка за столом осуществляется тестировщиком (не автором программы), который проверяет текст программы по списку часто встречающихся ошибок и "пропускает" через программу тестовые данные.
Метод оценки программ направлен на повышение качества ПС и для поиска ошибок не применяется. Поэтому в данной лабораторной работе он не рассматривается.
Пример: язык паскаль, текст программы:
Program MenuDemo;
Uses MenuUnit, Crt;
Var
Choice: Integer;
M: BBMenu;
BEGIN
CLRSCR;
M.Init( 25, 7, 18);
M.AddPrompt(Open a New File)
M.AddPrompt(Existing File);
M.AddPrompt(Close A File)
M.AddPrompt(Quit);
Choice:= M.GetChoice;
GotoXY(1, 24); ClrEol;
Case Choice OF
‘0’: Writeln(No Choice Was Made);
‘1’: Writeln(Procedure NewFile Should Be Called);
‘2’: Writeln(Procedure ExistingFile Should Be Called);
‘3’: Writeln(Procedure CloseFile Should Be Called);
‘4’: Writeln(Quitting ...);
END;
M.Done;
END.
Ошибка Тип ошибки
Case Choice OF
‘0’: Writeln(No Choice Was Made);
‘1’: Writeln(Procedure NewFile Should Be Called);
‘2’: Writeln(Procedure ExistingFile Should Be Called);
‘3’: Writeln(Procedure CloseFile Should Be Called);
‘4’: Writeln(Quitting ...);
1) Некорректное сравнение переменных разных типов (integer и string)
2) Некорректное использование оператора case (не применяется к строкам).
M.AddPrompt(Close A File) Отсутствует «;» после оператора
2.3. Структурное тестирование
Структурное тестирование называют также тестированием по «марш-рутам», так как в этом случае тестовые наборы формируют путем анализа маршрутов, предусмотренных алгоритмом. Под маршрутами при этом по-нимают последовательности операторов программы, которые выполняются при конкретном варианте исходных данных.
В основе структурного тестирования лежит концепция максимально полного тестирования всех маршрутов программы. Так, если алгоритм про-граммы включает ветвление, то при одном наборе исходных данных может быть выполнена последовательность операторов, реализующая действия, ко-торые предусматривает одна ветвь, а при втором - другая. Соответственно, для программы будут существовать маршруты, различающиеся выбранным при ветвлении вариантом.
Считают, что программа проверена полностью, если с помощью тестов удается осуществить выполнение программы по всем возможным маршру¬там передач управления. Однако нетрудно видеть, что даже в программе среднего уровня сложности число неповторяющихся маршрутов может быть очень велико, и, следовательно, полное или исчерпывающее тестирование маршрутов, как правило, невозможно.
Формирование тестовых наборов для тестирования маршрутов может осуществляться по нескольким критериям:
покрытие операторов. Критерий покрытия операторов подразумевает такой подбор тестов, чтобы каждый оператор программы выполнялся, по крайней мере, один раз. Это необходимое, но недостаточное условие для приемлемого тестирования.;
покрытие решений (переходов); Для реализации этого критерия не-обходимо такое количество и состав тестов, чтобы результат проверки каж-дого условия (т.е. решение) принимал значения «истина» или «ложь», по крайней мере, один раз. Нетрудно видеть, что критерий покрытия решений удовлетворяет крите¬рию покрытия операторов, но является более «сильным».
покрытие условий; Критерий покрытия условий является еще более «сильным» по сравнению с предыдущими. В этом случае формируют некоторое количество тестов, достаточное для того, чтобы все возможные резуль¬таты каждого условия в решении были выполнены, по крайней мере, один раз. Однако, как и в случае покрытия решений, этот критерий не всегда при¬водит к выполнению каждого оператора, по крайней мере, один раз. К крите¬рию требуется дополнение, заключающееся в том, что каждой точке входа управление должно быть передано, по крайней мере, один раз.
покрытие решений/условий Согласно этому методу тесты должны составляться так, чтобы, по крайней мере, один раз выполнились все воз-можные результаты каждого условия и все результаты каждого решения, и каждому оператору управление передавалось, по крайней мере, один раз.
комбинаторное покрытие условий. Этот критерий требует создания такого множества тестов, чтобы все возможные комбинации результатов ус-ловий в каждом решении и все операторы выполнялись, по крайней мере, один раз.
Пример.
Требуется выполнить структурное тестирование текста программы, которая определяет значение х в зависимости от значений параметров процедуры.
Procedure т (a, b:real; var x:real); begin
if(a>l) and (b=0) then x:=x /a;
if(a=2) or (x>l) then x:=x+1; end;
Для формирования тестов программу представляют в виде графа, вершины которого соответствуют операторам программы, а дуги представляют возможные варианты передачи управления (рис.1).
Рис. 1 - Схема алгоритма процедуры (слева) и ее граф передач управления (6).
Покрытие операторов будет реализовано при а = 2, b = 0, х = 3.
Однако, хотя исходные данные заданы так, чтобы все операторы программы были выполнены хотя бы один раз, для проверки программы этого явно недостаточно. Например, из второго условия следует, что переменная х может принимать любое значение, и в некоторых версиях языка Pascal это значение проверяться не будет. Кроме того, если при написании программы в первом условии указано, что (а > 1) or (b = 0), или, если во втором условии вместо х > 1 записано х > 0, то эти ошибки обнаружены не будут. Также существует путь 1-2-4-6, в котором х вообще не меняется и, если здесь есть ошибка, она не будет обнаружена.
По методу покрытия решений (переходов) рассматриваемую программу можно протестировать двумя тестами, покрывающими либо пути: 1—2—4—6, 1-2-3-4-5-6, либо пути: 1-2-3-4-6, 1-2-4-5-6, например:
а = 3, b = 0, х = 3 — путь 1-2-3-4-5-6;
а = 2, b = 1 , х = 1 — путь 1-2-4-5-6
Однако путь, где х не меняется, будет проверен с вероятностью 50 %: ес-ли во втором условии вместо х > 1 записано х < 1, то этими двумя тестами ошибка обнаружена не будет.
Покрытие условий проверяет че¬тыре условия:
l)a>l; 2)b = 0; 3)а = 2; 4)х>1.
Необходимо реализовать все возможные ситуации:
Тесты, удовлетворяющие этому условию:
а= 2, b = 0, х = 4 — путь 1-2-3-4-5-6, условия: 1 - да, 2 - да, 3-да, 4-да
а = 1, b = 1, х = 1 — путь 1-2-4-6, условия: 1 - нет, 2 - нет, 3 – нет, 4-нет.
Критерий покрытия условий часто удовлетворяет критерию покрытия решений, но не всегда. Тесты критерия покрытия условий для ранее рассмо-тренных примеров покрывают результаты всех решений, но это случайное совпадение. Например, тесты:
а=1,Ь = 0, х = 3 — путь 1-2-3-6, условия: 1 - нет, 2 - да, 3 - нет, 4 - да;
а = 2, b = 1, х = 1 — путь 1-2-3-4-5-6, условия: 1 - да, 2 - нет, 3 - да, 4 - нет
покрывают результаты всех условий, но только два из четырех результатов решений: не выполняется результат «истина» первого решения и результат «ложь» второго.
Основной недостаток метода – недостаточная чувствительность к ошиб-кам в логических выражениях.
Покрытие решений/условий.
Анализ, проведенный выше, показывает, что этому критерию удовлетво-ряют тесты:
а = 2, Ь = 0, х = 4 — путь 1-2-3-4-5-6, условия: 1 - да, 2 - да, 3 - да, 4 - да;
а=1,b=1,х=1— путь 1-2-4-6, условия; 1 - нет, 2 - нет, 3 - нет, 4 - нет.
Комбинаторное покрытие условий требует покрыть тестами восемь комбинаций:
1)а>1,b = 0; 5)а = 2,х>1;
2)а>1, b≠0; 6)а = 2, х<1;
3)а<1,b = 0; 7)а≠2, х>1;
4)а<1;b≠0 8) а≠ 2, х<1.
Эти комбинации можно проверить четырьмя тестами:
а = 2, b = 0, х = 4 — проверяет комбинации (1), (5);
а = 2, b = 1, х = 1 — проверяет комбинации (2), (6);
а= 1, b = 0, х = 2 — проверяет комбинации (3), (7);
а=1,Ь=1,х=1— проверяет комбинации (4), (8).
В данном случае то, что четырем тестам соответствует четыре пути, яв-ляется совпадением. Представленные тесты не покрывают всех путей, на-пример, acd. Поэтому иногда необходима реализация восьми тестов.
Таким образом, для программ, содержащих только одно условие на каж-дое решение, минимальным является набор тестов, который проверяет все результаты каждого решения и передает управление каждому оператору, по крайней мере, один раз.
Для программ, содержащих вычисления, каждое из которых требует проверки более чем одного условия, минимальный набор тестов должен:
• генерировать все возможные комбинации результатов проверок условий для каждого вычисления;
• передавать управление каждому оператору, по крайней мере, один раз.
Термин «возможных» употреблен здесь потому, что некоторые комбина-ции условий могут быть нереализуемы. Например, для комбинации k<0 и k>40 задать k невозможно.
2.4. Функциональное тестирование
Одним из способов проверки программ является тестирование с управ-лением по данным или по принципу «черного ящика». В этом случае про-грамма рассматривается как «черный ящик», и целью тестирования является выяснение обстоятельств, в которых поведение программы не соответствует спецификации.
Для обнаружения всех ошибок в программе, используя управление по данным, необходимо выполнить исчерпывающее тестирование, т. е. тестиро-вание на всех возможных наборах данных. Для тех же программ, где испол-нение команды зависит от предшествующих ей событий, необходимо прове-рить и все возможные последовательности. Очевидно, что проведение исчер-пывающего тестирования для подавляющего большинства случаев невоз-можно. Поэтому обычно выполняют «разумное» или «приемлемое» тестиро-вание, которое ограничивается прогонами программы на небольшом под-множестве всех возможных входных данных. Этот вариант не дает гарантии отсутствия отклонений от спецификаций.
Правильно выбранный тест должен уменьшать, причем более чем на единицу, число других тестов, которые должны быть разработаны для обес-печения требуемого качества программного обеспечения.
При функциональном тестировании различают следующие методы фор-мирования тестовых наборов:
• эквивалентное разбиение;
• анализ граничных значений;
• анализ причинно-следственных связей;
• предположение об ошибке.
Эквивалентное разбиение. Метод эквивалентного разбиения заключа-ется в следующем. Область всех возможных наборов входных данных про-граммы по каждому параметру разбивают на конечное число групп - классов эквивалентности. Наборы данных такого класса объединяют по принципу обнаружения одних и тех же ошибок: если набор какого-либо класса обнару-живает некоторую ошибку, то предполагается, что все другие тесты этого класса эквивалентности тоже обнаружат эту ошибку и наоборот.
Разработку тестов методом эквивалентного разбиения осуществляют в два этапа: на первом выделяют классы эквивалентности, а на втором - фор-мируют тесты.
Выделение классов эквивалентности является эвристическим процес¬сом, однако целесообразным считают выделять в отдельные классы эквива-лентности наборы, содержащие допустимые и недопустимые значения неко-торого параметра. При этом существует ряд правил:
• если некоторый параметр х может принимать значения в интервале
[1, 999], то выделяют один правильный класс 1 < х < 999 и два неправильных:
х < 1 и х > 999;
• если входное условие определяет диапазон значений порядкового ти¬па, например, «в автомобиле могут ехать от одного до шести человек», то определяется один правильный класс эквивалентности и два неправильных: ни одного и более шести человек;
• если входное условие описывает множество входных значений и есть
основания полагать, что каждое значение программист трактует особо, на¬
пример, «типы графических файлов: bmp, jpeg, vsd», то определяют правильный класс эквивалентности для каждого значения и один неправильный класс, например, txt;
• если входное условие описывает ситуацию «должно быть», например,
«первым символом идентификатора должна быть буква», то определяется
один правильный класс эквивалентности (первый символ - буква) и один не-правильный (первый символ - не буква);
• если есть основание считать, что различные элементы класса эквивалентности трактуются программой неодинаково, то данный класс разбивает¬ся на меньшие классы эквивалентности.
Таким образом, классы эквивалентности выделяют, перебирая ограниче-ния, установленные для каждого входного значения в техническом задании или при уточнении спецификации. Каждое ограничение разбивают на две или более групп. При этом используют специальные бланки - таблицы клас¬сов эквивалентности:
Ограничение на значение параметра Правильные классы эквивалентности Неправильные классы эквивалентности
Правильные классы включают правильные данные, неправильные клас¬сы - неправильные данные. Для правильных и неправильных классов тесты проектируют отдельно. При построении тестов правильных классов учиты-вают, что каждый тест должен проверять по возможности максимальное ко-личество различных входных условий. Такой подход позволяет минимизиро-вать общее число необходимых тестов. Для каждого неправильного класса эквивалентности формируют свой тест. Последнее обусловлено тем, что оп-ределенные проверки с ошибочными входами скрывают или заменяют дру¬гие проверки с ошибочными входами.
Анализ граничных значений. Граничные значения - это значения на границах классов эквивалентности входных значений или около них. Анализ показывает, что в этих местах резко увеличивается возможность обнаруже¬ния ошибок. Например, если в программе анализа вида треугольника было записано А + В > С вместо А + В > С, то задание граничных значений приве-дет к ошибке: линия будет отнесена к одному из видов треугольника.
Применение метода анализа граничных значений требует определенной степени творчества и специализации в рассматриваемой проблеме. Тем не менее, существует несколько общих правил для применения этого метода:
• если входное условие описывает область значений, то следует построить тесты для границ области и тесты с неправильными входными данными
для ситуаций незначительного выхода за границы области, например, если
описана область [-1.0, +1.0], то должны быть сгенерированы тесты: -1.0,
+1.0,-1.001 и +1.001;
• если входное условие удовлетворяет дискретному ряду значений, то
следует построить тесты для минимального и максимального значений и те-сты, содержащие значения большие и меньшие этих двух значений, напри¬
мер, если входной файл может содержать от 1 до 255 записей, то следует
проверить О, 1, 255 и 256 записей;
• если существуют ограничения выходных значений, то целесообразно
аналогично тестировать и их: конечно не всегда можно получить результат
вне выходной области, но тем не менее стоит рассмотреть эту возможность;
• если некоторое входное или выходное значение программы является
упорядоченным множеством, например, это последовательный файл, линей-ный список или таблица, то следует сосредоточить внимание на первом и по-следнем элементах этого множества.
Помимо указанных граничных значений, целесообразно поискать дру¬гие.
Анализ граничных значений, если он применен правильно, является од-ним из наиболее полезных методов проектирования тестов. Однако следует помнить, что граничные значения могут быть едва уловимы и определение их связано с большими трудностями, что является недостатком этого метода.
Оба описанных метода основаны на исследовании входных данных. Они не позволяют проверять результаты, получаемые при различных сочетаниях данных. Для построения тестов, проверяющих сочетания данных, применя¬ют методы, использующие булеву алгебру.
Анализ причинно-следствениых связей. Анализ причинно-следствен¬ных связей позволяет системно выбирать высокорезультативные тесты. Ме¬тод использует алгебру логики и оперирует понятиями «причина» и «следст¬вие». Причиной в данном случае называют отдельное входное условие или класс эквивалентности. Следствием - выходное условие или преобразование системы. Идея метода заключается в отнесении всех следствий к причинам, т. е. в уточнении причинно-следственных связей. Данный метод дает полез-ный побочный эффект, позволяя обнаруживать неполноту и неоднозначность исходных спецификаций.
Построение тестов осуществляют в несколько этапов. Сначала, посколь¬ку таблицы причинно-следственных связей при применении метода к боль¬шим спецификациям становятся громоздкими, спецификации разбивают на «рабочие» участки, стараясь по возможности выделять в отдельные таблицы независимые группы причинно-следственных связей. Затем в спецификации определяют множество причин и следствий.
Далее на основе анализа семантического (смыслового) содержания спе-цификации строят таблицу истинности, в которой каждой возможной комби-нации причин ставится в соответствие следствие. При этом целесообразно истину обозначать «1», ложь - «О», а для обозначения безразличных состоя-ний условий применять обозначение «X», которое предполагает произволь¬ное значение условия (0 или 1). Таблицу сопровождают примечаниями, зада-ющими ограничения и описывающими комбинации причин и/или следствий, которые являются невозможными из-за синтаксических или внешних огра-ничений. При необходимости аналогично строится таблица истинности для класса эквивалентности.
И, наконец, каждую строку таблицы преобразуют в тест. При этом реко-мендуется по возможности совмещать тесты из независимых таблиц.
Данный метод позволяет строить высокорезультативные тесты и обна-руживать неполноту и неоднозначность исходных спецификаций. Его недо-статком является неадекватное исследование граничных значений.
Предположение об ошибке. Часто программист с большим опытом на-ходит ошибки, «не применяя никаких методов». На самом деле он подсозна-тельно использует метод «предположение об ошибке».
Процедура метода предположения об ошибке в значительной степени основана на интуиции. Основная его идея заключается в том, чтобы пере-числить в некотором списке возможные ошибки или ситуации, в которых они могут появиться, а затем на основе этого списка составить тесты. Другими словами, требуется перечислить те особые случаи, которые могут быть не учтены при проектировании.
Проиллюстрируем применение всех рассмотренных выше методов на примере.
Пример. Пусть необходимо выполнить тестирование программы, определяющей точку пересечения двух прямых на плоскости. При этом она должна определять параллельность прямой одной из осей координат.
В основе программы лежит решение системы линейных уравнений:
Ах + By = С, Dx + Еу = F.
По методу эквивалентных разбиений формируем для каж¬дого коэффициента один правильный класс эквивалентности (коэффициент-вещественное число) и один неправильный (коэффициент - не вещественное число). Откуда генерируем 7 тестов:
1) все коэффициенты - вещественные числа (1 тест);
2-7) поочередно каждый из коэффициентов - не вещественное число (6 тестов).
По методу граничных значений можно считать, что для исход¬ных данных граничные значения отсутствуют, т. е. коэффициенты - «любые» вещественные числа. Для результатов получаем, что возможны варианты: единственное решение, прямые сливаются - множество решений, прямые параллельны - отсутствие решений. Следовательно, целесообразно предло-жить тесты с результатами внутри областей возможных значений результа-тов:
8) результат - единственное решение (δ ≠ 0);
9) результат - множество решений (δ = 0 и δ Х = δ у = 0);
10) результат - отсутствие решений (δ = 0, но δ Х ≠ 0 или δ у ≠ 0);
и с результатами на границе:
11) δ = 0,01;
12) δ = -0,01;
13) δ = 0, δ Х = 0,01, δ =0;
14) δ = 0, δ у =-0,01, δ Х = 0.
По методу анализа причинно-следственных связей определяем множество условий:
а) для определения типа прямой:
- для определения типа и существования первой прямой;
- для определения точки пересечения
Выделяем три группы причинно-следственных связей (определение ти¬па и существования первой линии, определение типа и существования вто¬рой линии, определение точки пересечения) и строим таблицы истинности для определения типа первой прямой (табл. 4.1) и для определения результа¬та (табл. 4.2). В обеих таблицах X означает неопределенное значение. Для второй прямой таблица истинности будет выглядеть аналогично табл. 4.1.
Каждая строка этих таблиц преобразуется в тест. При возможности (с учетом независимости групп) берутся данные, соответствующие строкам сразу двух или всех трех таблиц.
Таблица 4.1
А=0 в=о с=о Результат
0 0 X прямая общего положения
0 1 0 прямая, параллельная оси ОХ
0 1 1 ось ОХ
1 0 0 прямая, параллельная оси ОУ
1 0 1 ось ОУ
1 1 X множество точек плоскости
Таблица 4.2
δ = 0 δ Х = 0 δ у = 0 Единственное
решение Множество
решений Решения нет
0 X Х 1 1 0 0
1 0 X 0 0 1
1 X 0 0 0 1
1 1 1 0 1 0 0
В результате к уже имеющимся тестам добавляются:
15-21) проверки всех случаев расположения обеих прямых - 6 тестов по первой прямой совмещают с 6-ю тестами по второй прямой так, чтобы вари¬анты не совпадали (6 тестов);
22) проверка несовпадения условия 8Х = 0 или 8у = 0 (в зависимости от
того, какой тест был выбран по методу граничных условий) - тест также
можно совместить с предыдущими 6-ю тестами.
По методу предположения об ошибке добавим тест:
23) все коэффициенты - нули.
Всего получили 23 теста по всем четырем методам. Для каждого теста перед применением необходимо указать ожидаемый результат. Если попро¬бовать вложить независимые проверки, то, возможно, число тестов можно еще сократить.
2.5. Тестирование модулей
При тестировании модулей программного обеспечения, так же, как при проектировании и кодировании возможно при¬менение как восходящего, так и нисходящего подходов.
Восходящее тестирование. Восходящий подход предполагает, что каж¬дый модуль тестируют отдельно на соответствие имеющимся спецификаци¬ям на него, затем собирают оттестированные модули в модули более высокой степени интеграции и тестируют их. При этом проверяют межмодульные ин¬терфейсы, используемые для подключения модулей более низкого уровня ие¬рархии. И так далее, пока не будет собран весь программный продукт (рис. 5.1).
Такой подход обеспечивает полностью автономное тестирование, для которого просто генерировать тестовые последовательности, которые пере¬даются в модуль напрямую. Однако он имеет и существенные недостатки. Во-первых, при восходящем тестировании так же, как при восходящем про¬ектировании, серьезные ошибки в спецификациях, алгоритмах и интерфейсе могут быть обнаружены только на завершающей стадии работы над проектом. Во-вторых, для того, чтобы тестировать модули нижних уровней, необ¬ходимо разработать специальные тестирующие программы, которые обеспе¬чивают вызов интересующих нас модулей с необходимыми параметрами. Причем эти тестирующие программы также могут содержать ошибки.
Рисунок 5.1. - Тестирование программного обеспечения при восходящем подходе: а - автономное тестирование модулей нижнего уровня; б - тестирование следующего уровня
Нисходящее тестирование. Нисходящее тестирование органически связано с нисходящим проектированием и разработкой: как только проектирование какого-либо модуля заканчивается, его кодируют и передают на тес¬тирование.
В этом случае автономно тестируется только основной модуль. При его тестировании все вызываемые им модули заменяют модулями, которые в той или иной степени имитируют поведение вызываемых модулей (рис. 5.2). Та¬кие модули принято называть «заглушками». В отличие от тестирующих программ заглушки очень просты, например, они могут просто фиксировать, что им передано управление. Часто заглушки просто возвращают какие-либо фиксированные данные.
Как только тестирование основного модуля завершено, к нему подклю¬чают модули, непосредственно им вызываемые, и необходимые заглушки, а затем проводят их совместное тестирование. Далее последовательно под¬ключают следующие модули, пока не будет собрана вся система.
Основной недостаток нисходящего тестирования - отсутствие авто¬номного тестирования модулей. Поскольку модуль получает данные не непо¬средственно, а через вызывающий модуль, то гораздо сложнее обеспечить его «достаточное» тестирование.
Основным достоинством данного метода является ранняя проверка ос¬новных решений и качественное многократное тестирование сопряжения модулей в контексте программного обеспечения. При нисходящем тестиро¬вании есть возможность согласования с заказчиком внешнего вида (интер¬фейса) программного обеспечения.
Рисунок 5.2. - Начальные этапы тестирования:
а - основного модуля; б - двух модулей
Комбинированный подход. Чаще всего применяют комбинированный подход: модули верхних уровней тестируют нисходящим способом, а моду¬ли нижних уровней - восходящим. Этот способ позволяет с одной стороны начать с тестирования интерфейса, с другой - обеспечивает качественное ав¬тономное тестирование модулей низших уровней.
3. ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ
1. Ознакомиться с теоретической частью.
2. Применение ручного контроля. Проанализируйте заданный фрагмент программы (приложение Б) по списку вопросов для выявления исторически сложившихся общих ошибок программирования (приложение А). Укажите, к какому типу относятся найденные вами ошибки. Вариант задания получите у преподавателя. Пример решения аналогичного задания найдете в теоретической части
3. Изучение структурного тестирования. Сформируйте тестовые наборы для тестирования маршрутов фрагментов (приложение В) по заданным критериям. Проанализируйте целесообразность каждого из критериев для своей программы, укажите их недостатки, достоинства и преимущества над другими критериями. Пример выполнения аналогичного задания найдете в теоретической части.
4. Изучение функционального тестирования. Получите свой вариант у преподавателя. Запустите программу "Геометрические фигуры" (файл geometry.exe), руководство пользователя на программу находится в приложении Г. Нажмите на кнопку с названием своего варианта. Для своей программы сформируйте наборы тестов методами:
- эквивалентного разбиения;
- анализа граничных значений;
- анализа причинно-следственных связей.
Протестируйте программу на полученных тестовых наборах. Результаты тестирования оформите в таблицу:
Пример решения аналогичного задания смотрите в теоретической части.
Таблица 3.1 - Результаты тестирования
Тест Ожидаемый результат Полученный результат
5. Тестирование модулей. Для изучения нисходящего и восходящего тестирования предлагаются с программы: «Калькулятор» и «Обучающая программа», состаящие из нескольких модулей. Структуры этих программ представлены на рисунках 4.1 и 4.2. в приложении Д.
Возьмите у преподавателя вариант задания на тестирование одной из веток программы. Протестируйте каждый модуль ветки и оформьте результаты тестирования в таблицу. Также сформулируйте предложения по улучшению работы модулей.
Программа: «Калькулятор» и все составляющие ее модули находятся в папке «Калькулятор». «Обучающая программа» и все составляющие ее модули находятся в папке «Обучающая программа».
6. Составить отчет по выполненной работе.
приложение А
Пример списка во¬просов, который можно использовать при анализе правильности программ, написанных на языке Pascal, приведен в
1. Контроль обращений к данным
• Все ли переменные инициализированы?
• Не превышены ли максимальные (или реальные) размеры массивов и строк?
• Не перепутаны ли строки со столбцами при работе с матрицами?
• Присутствуют ли переменные со сходными именами?
• Используются ли файлы? Если да, то при вводе из файла проверяется ли за¬вершение файла?
• Соответствуют ли типы записываемых и читаемых значений?
• Использованы ли нетипизированные переменные, открытые массивы, дина¬мическая память? Если да, то соответствуют ли типы переменных при «наложении» формата? Не выходят ли индексы за границы массивов?
2. Контроль вычислений
• Правильно ли записаны выражения (порядок следования операторов)?
• Корректно ли выполнены вычисления над неарифметическими переменными?
• Корректно ли выполнены вычисления с переменными различных типов (в том числе с использованием целочисленной арифметики)?
• Возможно ли переполнение разрядной сетки или ситуация машинного нуля?
• Соответствуют ли вычисления заданным требованиям точности?
• Присутствуют ли сравнения переменных различных типов?
3. Контроль передачи управления
• Будут ли корректно завершены циклы?
• Будет ли завершена программа?
• Существуют ли циклы, которые не будут выполняться из-за нарушения усло¬вия входа? Корректно ли продолжатся вычисления?
• Существуют ли поисковые циклы? Корректно ли отрабатываются ситуации
«элемент найден» и «элемент не найден»?
4. Контроль межмодульных интерфейсов
• Соответствуют ли списки параметров и аргументов по порядку, типу, единицам измерения?
• Не изменяет ли подпрограмма аргументов, которые не должны изменяться?
• Не происходит ли нарушения области действия глобальных и локальных пе¬ременных с одинаковыми именами?
приложение Б
Варианты заданий для ручного тестирования
Вариант Язык Текст программы
1 Паскаль unit
NewParam;
interface
function ParamCount(): Word;
function ParamStr (Index: Integer): string;
function GetParamsStarting (Index: Integer): string;
implementation
uses
Objects,
TPString;
var
CommandLine: PString;
Params: array [Byte] of record L, R: Byte; end;
LocParamCount: Byte;
function ParamCount;
begin
ParamCount := LocParamCount;
end;
function ParamStr;
begin
if (index = =0)
then ParamStr := System.ParamStr (0)
else with Params [Index-1] do ParamStr := Copy (CommandLine^, L, R-L);
end;
function GetParamsStarting;
begin
with Params [Index-1] do GetParamsStarting := Copy (CommandLine^, L, $FF);
end;
const
ParamDelims = [ , #9];
Quotes = ["];
var
WaitForQuote: Boolean;
B: Byte;
begin
CommandLine := Ptr (PrefixSeg, $80);
LocParamCount := 0;
B := 0;
while B <= Length (CommandLine^) do
begin
Inc (B);
if CommandLine^ [B] in ParamDelims then Continue;
with Params [LocParamCount] do
begin
WaitForQuote := CommandLine^ [B] in Quotes;
if WaitForQuote then Inc (B)
W:= B;
while not (
(B > Length (CommandLine^)) or
WaitForQuote and (CommandLine^ [B] in Quotes) or
not WaitForQuote and (CommandLine^ [B] in ParamDelims)) do
Inc (B);
R := B;
if WaitForQuote then Inc (B);
end;
Inc (LocParamCount);
end;
end.
2 Паскаль program var1;
const x=12;
var
a : integer;
b: byte;
c: char;
arr: array [0..50] of real;
f: file of char;
begin
readln(a,b);
if (c=x)
begin
a:=a+1;
c:=c-“123”;
end;
for а:=0 to 100 do
begin
arr[b]=sqrt(b);
b:=b*3;
a:=-b/2;
end;
assign(f, “test.txt”);
{$I-};
reset(f);
{$I+};
if (IOResult<>0) then writeln (файл f не существует); else erase(f);
if (a>0) then return 1
else return –a;
end;
3 Паскаль program var[2];
const сon=”222”;
var
a : integer;
b: byte;
c: char;
b: boolean;
arr: array [-10..10] of real;
procedure Srednee (var x1, x2, x3: integer)
begin
if (x1=x2=x3=0) then sred:=0
else
if (x2=x3=0) then sred:=x1;
else
if (x3=0) then c:=(x1+x2)/2 else sred =(x1+x2+x3)/3;
return sred;
end;
begin
b:=false;
writeln (‘Введите значения’);
readln(a,b);
if (d>0)
begin
b:= Srednee (1,2,6);
end;
while not (b) do
begin
arr[d]:= arr[d]+4;
d:=d+1;
end;
Srednee (d, arr[1], a);
a:=sred;
end;
4 Паскаль unit inifile;
interface
type
string127 = string[127];
procedure getinistring(var inifile : text; name : string127; var value : string127);
procedure getconfig(configfile : string; var value:array of string127);
implementation
procedure getinistring;
var
buf : string127;
i, j : byte;
begin
value := 0;
reset(inifile);
while not seekeof(inifile) do
begin
readln(inifile, buf);
if ((buf[1] <> ;) and (buf[1] <> %)) then
begin
i := -1;
while (buf[i] = ) or (buf[i] = #9) do inc(i);
if (pos(name, buf) = i) then
begin
i := i+length(name);
while (buf[i] = ) or (buf[i] = #9) do inc(i);
j := 1;
while ((buf[i+j] <> ;) and buf[i+j] <> %))
and (j < ord(buf[0])-i+1) do inc(j);
value := copy(buf, i, j);
exit(); end;
end;
end;
end;
procedure getconfig;
var
config : text;
I : integer;
buf : pointer;
begin
i := ioresult;
assign(config, configfile);
getmem(buf, 5120);
settextbuf(config, buf^, 5120);
reset(config);
i := ioresult;
if i <> 0 then
begin
writeln(Error reading file: , configfile);
writeln(Program aborted);
halt(20);
end;
for j := low(value) to high(value) do getinistring(config, value[j], value[j]);
close(config);
freemem(buf, 5120);
end;
end.
5 С++ #include "stdafx.h"
#include "iostream.h"
int m,n;
int* ostatki;
int* period;
unsigned int len;
bool IsInOstatki(int ost,int len)
{
for(int i=0;i<len-1;i++)//len-1 чтоб не проверять остаток, ввденый на
текущем шаге
{
if(ostatki[i+j]==ost)return i;
}
return 0;
}
int main(int argc, char* argv[])
{
cout<<"Vvedite chislitel drobi
";
cin>>m;
cout<<"
vvedite znamenatel drobi
";
cin>>n;
ostatki=new int[n+1];
period=new int[n+1];
int d=m,q=n;
int r,pos;
len=1;
do
{
r=d%q
ostatki[len]=r;
d/=q;
period[len]=d;
d=10*r;
len++;
}
while((pos=IsInOstatki(r,len)=0);
cout<<"drob ravna
";
cout<<period[1]<<",";
for(r=2;r<len;r++)
{
cout<<period[r];
}
cout<<"
period raven ";
for(r=pos+1;r<len;r++)
cout<<period[r];
cout>>"
";
delete[] ostatki;
delete[] period;
return 0;
}
6 С++ #include "string.h"
#include "math.h"
#include "stdafx.h"
#include "calc.h"
#include "ctype.h"
class calc
{
public:
double proceed();
void SetFunction(char* lpszCommand);
calc(char* lpszCommand);
calc();
struct SyntaxError
{
const char* p;
SyntaxError(const int* q){p=q;} разные типы
};
struct MathError
{
const char* p;
MathError(const char* q){p=q;}
};
virtual ~calc();
private:
Tok_Type CurrTok;
const bool InSet=true;
protected:
char* pCurrPos;
char TokenValue[16];
double add_sub();
char Function[255];
virtual double prim();
};
calc::calc()
{
memset(Function,0,256)
pCurrPos=Function;
}
calc::~calc()
{
}
calc::calc(char* lpszCommand)
{
SetFunction(lpszCommand);
}
void calc::SetFunction(char* lpszCommand)
{
strncpy(Function,lpszCommand,255);
Function[257]=0;
pCurrPos=Function;
InSet=false;
}
приложение В
Варианты заданий для структурного тестирования.
Критерии тестирования маршрутов:
1. покрытия операторов;
2. покрытия решений (переходов);
3. покрытия условий;
4. покрытия решений/условий;
5. комбинаторного покрытия условий.
Вариант 1.
procedure m(a,b: real; var x: real)
begin
if (a>0)and(b<0) then x:=x+1;
if ((a=2)or(x>3))and(b>-10) then x:=x-1;
end;
Вариант 2.
procedure m(a,b,с: real; var x: real)
begin
if (a>0)and(b<0)and(x>6) then x:=x+1;
if (a=4)or(c<0) then x:=x11;
end;
Вариант 3.
procedure m(a,b: real; var x: real)
begin
if (a<=6)and(b<0) then x:=x+1;
if (a=7) then x:=x-1
else if (x>3) x=x*2;
end;
Вариант 4.
procedure m(a,b: real; var x: real)
begin
if (a>0)
if(b<0) then x:=x+1
else x=x*2;
if (a>2)or(x=0) then x:=x+1;
end;
приложение Г
ПРОГРАММА "ГЕОМЕТРИЧЕСКИЕ ФИГУРЫ" (ФАЙЛ GEOMETRY.EXE)
Руководство пользователя
Назначение и условия применения
Разрабатываемый программный продукт планируется использовать в рамках учебного процесса по теме «Методы тестирования» для обучения студентов функциональному тестированию. Предполагается использовать программу в компьютерных классах учебного заведения или на домашних ПК студентов.
Условия выполнения программы
Для использования данного программного продукта необходим компьютер под управлением операционной системы Windows. Минимальная конфигурация ПК: процессор – Pentium 2, или аналогичной производительности, оперативная память - 64 Мб, свободного места на диске – 500 Кб
Выполнение программы
Запуск программы осуществляется двойным щелчком по файлу geometry.exe. После запуска пользователю предоставляется выбрать вариант геометрической программы. Для выбора пользователь должен нажать на кнопку с названием нужного варианта.
При выборе первого варианта, выводится программа определения типа треугольника (прямоугольный, правильный, равнобедренный, остроугольный, тупоугольный). Предлагается ввести длины сторон треугольника в поля ввода, таких полей 3 (по числу сторон). При нажатии на кнопку «Проверить», должно выводится либо сообщение об ошибке ввода, либо один из возможных результатов, перечисленных выше.
При выборе второго варианта, выводится программа определения того, является ли четырехугольник квадратом. Предлагается ввести ординаты каждой вершины квадрата (всего 8 полей ввода). При нажатии на кнопку «Проверить», должно выводится либо сообщение об ошибке ввода, либо сообщение о том, является ли четырехугольник квадратом.
При выборе третьего варианта, выводится программа определения того, является ли четырехугольник ромбом. Предлагается ввести координаты каждой вершины квадрата (всего 8 полей ввода). При нажатии на кнопку «Проверить», должно выводится либо сообщение об ошибке ввода, либо сообщение о том, является ли четырехугольник ромбом.
При выборе четвертого варианта, выводится программа определения взаимного положения окружности и прямой. Предлагается ввести радиус окружности и параметры прямой (коэффициенты k и b). При нажатии на кнопку «Результат», должно выводится либо сообщение об ошибке ввода, либо сообщение «Прямая и окружность пересекаются», «Прямая и окружность не пересекаются», «Прямая – касательная к окружности».
При выборе пятого варианта, выводится программа определения взаимного положения окружности и прямой. Предлагается ввести радиусы окружностей и координаты их центров. При нажатии на кнопку «Результат», должно выводится либо сообщение об ошибке ввода, либо сообщение «Не пересекаются», «Совпадают», «Касаются в одной точке», «Пересекаются в двух точках».
Для завершения работы программы, нужно, если вы находитесь на форме выбора варианта, закрыть окно формы (нажатием крестика в правом верхнем углу). В остальных случаях нужно закрыть два окна – сначала с геометрической программой, затем с выбором варианта.
Сообщения оператору
В ходе выполнения программы никаких сообщений, кроме описанных выше, не выводится.
приложение Д
Тестирование модулей
Для изучения нисходящего и восходящего тестирования предлагаются программы: «Калькулятор» и «Обучающая программа», состаящие из нескольких модулей. Структуры этих программ представлены на рисунках 4.1 и 4.2.
Программа «Калькулятор»:
Рисунок 4.1 – Структура программы калькулятор
1 – главный модуль calc. Файл calc.dpr. Осуществляет выбор типа операции (тригонометрическая, арифметическая, возведения в степень) или выход из программы.
2 – модуль выбора конкретной арифметический операции Arithmetic. Файл Arithmetic.pas.Осуществляет выбор операции: +, -, /, *, если операция выбрана неправильно, то предлагает выбрать снова.
3 – модуль выбора конкретной тригонометрической операции trigonom. Файл trigonom.pas. Осуществляет выбор операции: sin, cos, tg, ctg, если операция выбрана неправильно, то предлагает выбрать снова.
4 – модуль выбора возведения в степень Stepen. Файл Stepen.pas. Осуществляет выбор операции: , если операция выбрана неправильно, то предлагает выбрать снова.
5 – модуль осуществления операции сложения Slozhenie. Файл Slozhenie.pas
6 – модуль осуществления операции вычитания Vychitanie. Файл Vychitanie.pas
7 – модуль осуществления операции умножения Umnozhenie. Файл Umnozhenie.pas
8 – модуль осуществления операции деления Delenie. Файл Delenie.pas
9 – модуль вичисление синуса sinus. Файл sinus.pas
10 – модуль вичисление косинуса cosinus. Файл cosinus.pas
11 – модуль вичисление тангенса tangens. Файл tangens.pas
12 – модуль вичисление котангенса cotangens. Файл cotangens.pas
13 – модуль вичисление квадрата числа Kvadrat. Файл Kvadrat.pas
14 – модуль вичисление куба числа Kub. Файл Kub.pas
15 – модуль вичисление Х в степень Y xStepY. Файл xStepY.pas
Обучающая программа:
Рисунок 4.1 – Структура программы «Обучающая программа»
Возьмите у преподавателя вариант задания на тестирование одной из веток программы. Протестируйте каждый модуль ветки и оформьте результаты тестирования в таблицу. Также сформулируйте предложения по улучшению работы модулей.
Программа: «Калькулятор» и все составляющие ее модули находятся в папке «Калькулятор».
«Обучающая программа» и все составляющие ее модули находятся в папке «Обучающая программа».
Пример тестирования модуля регистрации ветви «Модуль регистрации пользователя Registration - Главный модуль программы main.» методом восходящего тестирования.
Создаем новую среду. Для этого нужно создать и сохранить новый проект и присоединить к нему модуль регистрации. Имитируем вызов регистрации из нового модуля:
program Project1;
uses
Forms,
Unit1 in Unit1.pas {Form1},
Registration in Registration.pas;
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TRegist, Regist);
Application.Run;
end.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Registration;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
regist.ShowModal;
ShowMessage(fio);
end;
end.
Проводим тесты:
Тест Ожидаемый результат Полученный результат Вывод по тесту
Вводим ФИО и нажимаем кнопку «Регистрация» Вывод ФИО Вывод ФИО +
Не вводим ФИО и нажимаем кнопку «Регистрация» Возврат в форму регистрации Перешли к главному модулю, вывод пустой строки -
Не вводим ФИО и нажимаем кнопку закрытия окна регистрации вывод пустой строки (так как ничего не передано) вывод пустой строки +
Вводим ФИО и нажимаем кнопку закрытия окна регистрации вывод пустой строки (так как ничего не передано) вывод пустой строки +
Выводы и предложения: не передавать управления в главную форму, по кнопке «Регистрация», если не введены ФИО.
ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
Методические указания по предмету «Программирование»