Учебник по тестированию. Guide to Software Test Design
Скачать 2.51 Mb.
|
Введение Тестирование потока управления - это одна из двух техник тестирования белого ящика. С помощью данной техники тестирования определяются пути выполнения кода программного модуля, после чего создаются и исполняются тест-кейсы для покрытия этих путей. Вторая техника, рассматриваемая в следующей главе, фокусируется на потоке данных. Ключевой момент Путь - это последовательность выполнения операторов, начинающаяся на входе и заканчивающаяся на выходе. К несчастью, в любом стоящем интереса модуле попытка исчерпывающего тестирования всех путей потока управления имеет несколько значимых недостатков: ● Количество путей может быть огромным и, таким образом, непроверяемым в течение разумного периода времени. Каждое бинарное ветвление удваивает количество путей, а каждый цикл умножает количество путей на количество итераций в цикле. Например: ● for (i=1; i<=1000; i++) ● for (j=1; j<=1000; j++) ● for (k=1; k<=1000; k++) ● doSomethingWith(i,j,k); ● - doSomethingWith() выполняется миллиард раз (1000х1000х1000). Должен быть проверен каждый уникальный путь. ● Пути, описанные в спецификации, могут просто быть пропущены при реализации модуля. Любой подход тестирования, основанный на реализованных путях, никогда не найдет пути, которые не были реализованы. ● if (a>0) doIsGreater (); ● if (a==0) doIsEqual(); ● // отсутствует оператор "if (a<0) dolsLess ();" ● Даже если поток управления сам по себе является правильным, то дефекты могут быть в обрабатываемых операторах модуля. 107 ● // актуальный (но неправильный) код ● а=а+1 ● // правильный код ● a=a-1; ● Модуль может правильно работать почти для всех значений входных данных, но для некоторых ошибаться. ● int blech (int a, int b) { ● return a/b; ● } ● - будет правильно выполняться, если b не равно 0, но сломается, если b равно 0. Несмотря на то, что у тестирования потока управления есть ряд недостатков, оно по-прежнему является важным инструментом в арсенале тестировщика. Методика Граф потока управления Графы потока управления являются основой тестирования потока управления. Они позволяют документировать структуру управления модуля. Модули кода преобразуются в графы, пути в этих графах анализируются, и из этого анализа создаются тест-кейсы. Ключевой момент Графы потока управления являются основой тестирования потока управления. Графы потока управления состоят из ряда элементов: ● Блок процесса - представляет собой непрерывную последовательность программных операторов, которые выполняются последовательно от начала до конца. Запрещены все входы в блок, кроме начального. Запрещены все выходы из блока, кроме завершающего. После того, как блок начат, каждый операторов в нем будет выполнен последовательно. Блоки процесса представлены в графе потока управления с одной точкой входа и одной точкой выхода. 108 ● Точка альтернативы - это такая точка в модуле, в которой поток управления может измениться. В большинстве случаев точки альтернатив являются бинарными и реализуются в виде инструкций if-then-else. Ситуации, где имеется много альтернатив, реализуются с помощью оператора выбора Case. Они представлены с одной точкой входа и несколькими точками выхода. ● Точка соединения - это точка, в которой все потоки управления соединяются вместе. Следующий пример кода представляет граф связанного потока: 109 Рисунок 10-1: Граф потока, эквивалентный программному коду. Уровни тестового покрытия В тестировании потока управления определяются различные уровни тестового покрытия. Под "покрытием" имеется в виду отношение объема кода, который уже был проверен, к объему, который осталось проверить. В тестировании потока управления покрытие определяется в виде нескольких различных уровней. Заметим, что эти уровни покрытия представлены не по порядку. Это потому, что в некоторых случаях проще определить более высокий уровень покрытия, а затем определить более низкий уровень покрытия в условиях высокого. Уровень 1 Самым низким уровнем покрытия является " 100% покрытие операторов " (иногда слово "100%" отбрасывается и остается только "покрытие операторов"). Это означает, что каждый оператор модуля должно быть выполнен как минимум один раз. Несмотря на то, что это может показаться разумной идеей, на таком уровне покрытия может быть пропущено много дефектов. Рассмотрим следующий кусок кода: if (а>0) {х=x+1;} if (b==3) {y=0;} Этот код может быть представлен в графическом виде следующим образом: 110 Рисунок 10-2: Графическое представление фрагмента двухстрокового кода. Эти две строки кода реализуют четыре разных пути выполнения: Рисунок 10-3: Четыре пути выполнения. Для того, чтобы проверить каждую строку кода в этом модуле, достаточно одного тест-кейса (например, взять в качестве входных данных a=6 и b=3), но очевидно, что такой уровень покрытия пропустит проверку 111 многих других путей. Таким образом, покрытие операторов, как правило, не является приемлемым уровнем тестирования. Даже при условии, что покрытие операторов является самым низким уровнем покрытия, часто его трудно применить на практике. Часто в модулях есть код, который выполняется только в исключительных случаях - при нехватке памяти, заполнения диска, нечитаемых файлах, потерянной связи и т.д. Моделирование таких ситуаций тестировщики могут посчитать трудным или даже невозможным и, таким образом, код, который работает с этими проблемами, останется непроверенным. Для имитации многих из этих исключительных ситуаций можно воспользоваться инструментом Holodeck. В соответствии со спецификацией Holodeck, он "позволит вам, как тестировщику, проверить программное обеспечение, наблюдая за системными вызовами, и создать тест-кейсы, которые можно будет использовать во время выполнения программы для того, чтобы изменить поведение приложения. Изменения могут включать в себя манипуляции с параметрами, которые передаются функциям или изменение возвращаемых значений функций программы. Кроме того, также можно установить коды ошибок и другие системные события. Этот набор возможностей позволяет эмулировать различные окружения, которые могут возникнуть в вашей программе - отсюда и название "Holodeck". Вместо того, чтобы отключать подключение к сети, создавать диск с битыми секторами, искажать сетевые пакеты или запустить какое-то окно или особую манипуляцию на вашей машине, для имитации проблем вы можете использовать Holodeck. Holodeck позволит легко поместить неисправности в программное обеспечение, которое вы тестируете." Holodeck Для скачивания Holodeck перейдите по ссылке http://www.sisecure.com/holodeck/holodeck-trial.aspx Уровень 0 На самом деле, существует уровень покрытия ниже "100% покрытия операторов". Этот уровень определяется как " тестируй все, что протестируешь, пользователи протестируют остальное ”. Корпоративный ландшафт усыпан выцветшими на солнце костями организаций, которые использовали такой тестовый подход. Касательно этого уровня покрытия Борис Бейзер написал: "тестирование, меньшее чем это [100% покрытие операторов], для нового программного обеспечения является недобросовестным и должно быть признано преступлением. ... В случае, если я не ясно выразился, ... непроверенный код в системе - это глупо, недальновидно и безответственно". Уровень 2 Следующим уровнем покрытия потока управления является " 100% покрытие альтернатив ". Его также называют "покрытием ветвей". На этом уровне достаточно такого набора тестов, в котором каждый узел с ветвлением (альтернатива), имеющий TRUE или FALSE на выходе, оценивается как минимум один раз. В предыдущем примере это может быть достигнуто двумя тест-кейсами: (a=2, b=2 и a=4, b=3). 112 Рисунок 10-4: Два тест-кейса, которые дают 100% покрытие альтернатив. Для оператора выбора Case с несколькими выходами нужно создать тесты для каждого выхода. Замечу, что покрытие альтернатив не гарантирует покрытие всех путей, но при этом гарантирует покрытие всех операторов. Уровень 3 Не все условные операторы так просты, как было показано ранее. Рассмотрим более сложные операторы: if (a>0 && c==1) {x=х+1;} if (b==3 || d<0) {y=0;} Для получения TRUE на выходе первого оператора, требуется a больше нуля и c, равное 1. Второй операторой требует b, равный трём, или d меньше нуля. Если при тестировании первого оператора переменной a будет присвоено значение 0, то тогда не будет проверена вторая часть условия c==1. (В большинстве языков программирования второе выражение даже не оценивается.) Следующим уровнем покрытия потока управления является " 100% покрытие условий ". На этом уровне достаточно такого набора тест-кейсов, в котором каждое условие, имеющее TRUE и FALSE на выходе, выполнено как минимум один раз. Этот уровень покрытия может быть достигнут двумя тестами: a>0, c=1, b=3, d<0 и a≤0, с≠1, b≠3, d≥0. Как правило, покрытие условий лучше, чем покрытие альтернатив, потому что каждое отдельное условие проверяется как минимум один раз, в то время как покрытие альтернатив может быть достигнуто без проверки всех условий. Уровень 4 Рассмотрим ситуацию: if(x&&y;) {условныйОператор;} // примечание: && обозначает логическое И Покрытие условий можно достичь двумя тестами (х=TRUE, у=FALSE и х=FALSE, у=TRUE), но обратите внимание, что с таким набором входных данных условный оператор никогда не выполнится. При наличии подобных комбинаций условий для более полного покрытия может быть выбран следующий уровень - " 100% покрытие условий/альтернатив " . На этом уровне тест-кейсы создаются для каждого условия и для каждой альтернативы. 113 Уровень 5 Тщательнее учитывайте, как компилятор языка программирования на самом деле оценивает несколько условий в альтеративе. Применяйте эти знания для создания тест-кейсов по " 100% покрытию множественный условий ". if (а>0 && с==1) {х=х+1;} if (b==3 || d<0) {y=0;} // примечание: || обозначает логическое ИЛИ будет оценено как: Рисунок 10-5: Оценка множественных условий компилятором. Этот уровень покрытия может быть достигнут четырьмя тест-кейсами: а>0, c=1, b=3, d<0 a≤0, c=1, b=3, d≥0 a>0, c≠1, b≠3, d<0 a≤0, c≠1, b≠3, d≥0 Достижение 100% покрытия множественных условий обеспечивается покрытием условий, покрытием альтернатив и покрытием условий/альтернатив. Заметим, что покрытие множественных условий не гарантирует покрытие всех путей. Уровень 7 В завершении мы достигаем самого высокого уровня, который называется " 100% покрытие путей ". Для кода модулей без циклов количество путей, как правило, достаточно мало, поэтому на самом деле можно построить тест-кейсы для каждого пути. Для модулей с циклами количество путей может быть огромным, что представляет неразрешимую проблему тестирования. 114 Рисунок 10-6: Интересная диаграмма потока со многими, многими путями. Уровень 6 Если, в случае зацикливания, количество путей становится бесконечным, то имеет смысл существенно их сократить, ограничив количество циклов выполнения, что позволит уменьшить количество тестовых случаев. Первый вариант - не выполнять цикл совсем; второй - выполнить цикл один раз; третий - выполнить цикл n раз, где n - это небольшое значение, представляющее символическое количество повторений цикла; четвертый - выполнить цикл m раз, где m - максимальное количество повторений цикла. Кроме того, можно выполнить цикл m-1 и m+1 раз. Перед тем, как начинать тестирование потока управления, должен быть выбран соответствующий уровень покрытия. Структурное тестирование / Основной маршрут тестирования Обсуждение тестирования потока управления не будет полным без представления структурного тестирования, также известного как " основной маршрут тестирования". Структурное тестирование основано на новаторской работе Тома МакКейба. Для определения тест-кейсов используется анализ топологии графа потока управления. Процесс структурного тестирования состоит из следующих этапов: ● получение графа потока управления от программного модуля; ● вычисление цикломатической сложности графа (C); ● получение набора основных маршрутов, количество которых равно C; ● создание тест-кейса для каждого основного маршрута; ● выполнение этих тестов. Рассмотрим следующий граф потока управления: 115 Рисунок 10-7: Пример графа потока управления МакКейб определяет цикломатическую сложность (C) графа как: C = рёбра - узлы + 2 Рёбра - это стрелки, а узлы - это ключевые точки графа. У рассмотренного графа 24 ребра и 19 узлов, что соответствует цикломатической сложности, равной 24-19+2=7. В некоторых случаях вычисление цикломатической сложности может быть упрощено. Если все альтернативы в графе являются бинарными (т.е. у них есть ровно два исходящих ребра), и граф содержит р бинарных альтернатив, то: C = р +1 Цикломатическая сложность - это конечное минимальное количество независимых, нецикличных маршрутов (называемые основными маршрутами), которые могут образовывать все возможные линейные пути в программном модуле. С точки зрения графа потока, каждый основной маршрут проходит как минимум через одно ребро, в которое нет никакого другого пути. Техника структурного тестирования МакКейба призывает к созданию тестового набора, состоящего из C тест-кейсов - по одному для каждого основного маршрута. Важно! Создание и выполнение C-тестов, базирующихся на основных маршрутах, гарантирует покрытие как всех ветвей, так и всех операторов. Поскольку набор основных маршрутов покрывает все ребра и узлы графа потока управления, то структурное тестирование, удовлетворяющее этому критерию, автоматически гарантирует покрытие как всех ветвей, так и всех операторов. 116 Процесс создания набора основных маршрутов описывается МакКейбом следующим образом: 1. Выбрать "базовый" маршрут. Он должен быть достаточно "типичным" путем выполнения (а не путем обработки исключений). Лучше всего выбрать наиболее важный маршрут с точки зрения тестировщика. Рисунок 10-8: Выбран базовый основной маршрут ABDEGKMQS 2. Выбрать следующий путь , взяв другой результат первой в основном маршруте альтернативы, сохранив при этом максимальное количество других альтернатив этого основного маршрута. 117 Рисунок 10-9: Второй основной маршрут ACDEGKMQS 3. Сгенерировать третий путь : снова начать с базового маршрута, но выбрать другой результат второй альтернативы, а не первой. Рисунок 10-10: Третий основной маршрут ABDFILORS 118 4. Сгенерировать четвертый путь : снова начать с базового маршрута, но выбрать другой результат третьей альтернативы, а не второй. Продолжить построение путей, каждый раз выбирая разные результаты каждой альтернативы до тех пор, пока не достигнем низа графа. Рисунок 10-11: Четвертый основной маршрут ABDEHKMQS 119 Рисунок 10-12: Пятый основной маршрут ABDEGKNQS 5. Когда будут пройдены все альтернативы основного пути, перейти ко второму пути, проходя через его альтернативы одна за другой. Эту процедуру продолжают до тех пор, пока не будет получен полный набор основных маршрутов. 120 Рисунок 10-13: Шестой основной маршрут ACDFJLORS 121 Рисунок 10-14: Седьмой основной маршрут ACDFILPRS Таким образом, набором основных машрутов для этого графа являются: ● ABDEGKMQS ● ACDEGKMQS ● ABDFILORS ● ABDEHKMQS ● ABDEGKNQS ● ACDFJLORS ● ACDFILPRS Структурное тестирование призывает к созданию тест-кейсов для каждого из этих путей. Такой набор тестов гарантирует полное покрытие всех операторов и ветвей. Обратите внимание, что могут быть созданы несколько наборов основных маршрутов, которые не обязательно будут уникальными. Однако каждый набор будет таким, чтобы его тесты покрыли каждый оператор и каждую ветвь. Пример Рассмотрим следующий пример из веб-сайта "Браун и Дональдсон". Возьмем кусок кода, который определяет, должен ли БиД купить или продать определенные акции. К сожалению, принцип работы сайта является секретной коммерческой тайной, поэтому вместо настоящих операторов кода обработки были подставлены операторы типа s1, s2, и т.д. Операторы потока управления остались неизменными, но 122 вместо их настоящих условий были подставлены такие общие условия, как c1 и c2. (Вы же не думали, что мы на самом деле покажем вам, как можно узнать, стоит ли покупать или продавать акции?) Примечание s1, s2, ... представляют собой Java-операторы, а c1, c2, ... - условия. boolean evaluateBuySell (TickerSymbol ts) { s1; s2; s3; if (c1) {s4; s5; s6;} else {s7; s8;} while (c2) { s9; s10; switch (c3) { case-A: s20; s21; s22; break; // конец Case-A case-B: s30; s31; if (c4) { s32; s33; s34; } else { s35; } break; // конец Case-B case-C: s40; s41; break; // конец Case-C case-D: s50; break; // конец Case-D } // конец Switch 123 s60; s61; s62; if (c5) {s70; s71; } s80; s81; } // конец While s90; s91; s92; return result; Рисунок 10-15: Java код модуля evaluateBuySell сайта "Браун и Дональдсон". Этому Java коду соответствует следующая диаграмма потока управления: Рисунок 10-16: Граф потока управления для модуля evaluateBuySell сайта "Браун и Дональдсон". Цикломатическая сложность этой диаграммы вычисляется как: 124 ребра - узлы + 2 или 22-16+2=8 Давайте для простоты описания путей уберем код и просто пометим каждый узел. Рисунок 10-17: Граф потока управления для модуля evaluateBuySell сайта "Браун и Дональдсон". Набор из восьми основных маршрутов: 1. ABDP 2. ACDP 3. ABDEFGMODP 4. ABDEFHKMODP 5. ABDEFIMODP 6. ABDEFJMODP 7. ABDEFHLMODP 8. ABDEFIMNODP 125 Помните, что наборы основных маршрутов не являются уникальными - для графа может существовать несколько наборов основных маршрутов Данный набор основных маршрутов сейчас реализован в качестве тест-кейсов. Выберите значения для состояний, которые будут подходящими для каждого пути, и выполните тесты. 0>0>0> |