Лекции. Тестирование ПО (all in one) (1186159), страница 21
Текст из файла (страница 21)
Втестируемом компоненте две операции — положить деньги на счет и снять деньги со счета.Пусть снимаемая/заносимая сумма и текущий баланс счета представлены 32-битнымицелыми числами.•Выделение областей определения операций и их естественных подобластей даетследующие результаты.o Операция внесения денег на счет добавляет вносимую сумму к балансу длятех сумм и балансов, для которых результирующая сумма не превышаетмаксимального 32-битного числа.Если итоговый баланс может превысить максимум, эта операция не должнавыполнять никаких действий, но должна вернуть какое-то указание наслишком большую сумму на счете для выполнения этой операции.o Операция снятия денег со счета вычитает снимаемую сумму из баланса,если итог не станет меньше минимально возможного баланса,соответствующего максимально возможному кредиту.
Если кредит приэтом превышается, баланс не изменяется, а возвращается указание нанедостаточность суммы на счете для выполнения этой операции.Для каждой из операций выделены две подобласти различного поведения. Областьюопределения каждой из них служит все множество пар возможных значений балансаи вносимой/снимаемой суммы, которая должна быть положительной.Полученные области изображены на рисунках ниже.вносимая суммаInteger.MAX_VALUE-Integer.MAX_VALUE-maxCreditInteger.MAX_VALUEбалансРазбиение области определения для операции внесения денег на счет.
Границаголубого или красного цвета означает, что она включается в голубую или,соответственно, красную областьснимаемая суммаInteger.MAX_VALUE-Integer.MAX_VALUE••-maxCreditInteger.MAX_VALUEбалансРазбиение области определения для операции снятия денег со счета.Для обеих операций получилось по две подобласти, представленные на рисункахразными цветами. Голубым цветом выделена область нормального поведения,красным — область балансов и сумм, при которых выполнение операции внормальном режиме невозможно.В обоих случаях область голубая область имеет границу, состоящую из трехсегментов. Красные области в обоих случаях ограничены двумя сегментами исоприкасаются с одним из сегментов голубой области.Значения тестовых данных (в данном случае, включающие и значение баланса,являющееся состоянием тестируемого компонента), получаемые на основе эвристикипокрытия всех подобластей, границ и окрестностей границ, изображены дляоперации внесения денег на счет ниже (большие черные точки).
Для второй операцииони получаются аналогичным образом. Заметим, что больше всего тестовымиданными должны быть покрыты окрестности границы, разделяющей выделенныеподобласти.Некоторые выбранные точки (внутри голубой области вблизи голубой границы ивнутри красной области вблизи красных границ) не являются необходимыми с точкизрения эвристик доменного тестирования, но часто такие точки добавляются (вколичестве, не превосходящим число точек, выбранных по основной эвристике) дляотслеживания других видов ошибок.вносимая суммаInteger.MAX_VALUE-Integer.MAX_VALUE-maxCreditInteger.MAX_VALUEбалансБолее сложный пример.Рассмотрим программу решения квадратного уравнения ax2+bx+c=0.
Можно считать, чтопрограмма оформлена в виде функции int solveEquation(double a, double b, double c, double*x1, double *x2). Первые три ее параметра являются коэффициентами уравнения, последниедва предназначены для возвращения его решений, а целочисленный результат — этоколичество решений.a = 0,Условиеa=b=a=a ≠ 0, b2a ≠ 0, b2a ≠ 0, b2с=0b = 0,4ac < 04ac = 04ac > 0b≠0с≠001012Число∞решенийРезультат–101012Таблица 1. Подобласти области определения программы, решающей квадратноеуравнение.Если решений два, возвращается 2, и указатели на оба решения.
Если есть только однорешение, возвращается 1 и оба указателя x1 и x2 указывают на одно и то же значение. Еслирешений нет, возвращается 0, и значения по указателям произвольны. Если решенийбесконечно много, возвращается –1.Для этой функции четко выделены подмножества области определения, представленные втаблице выше. Они определяют разбиение ее пространства параметров на 4 подобласти спомощью конуса нулевого дискриминанта и плоскости нулевого первого коэффициента иеще 7 компонентов их границ.
Итого выделяется 11 подобластей и элементов границ.•внутренность конуса b2 = 4ac сверху от плоскости a = 0 — a > 0 и b2-4ac > 0;•внутренность конуса снизу от плоскости — a < 0 и b2-4ac > 0;•вне конуса и сверху от плоскости — a > 0 и b2-4ac < 0;•вне конуса и снизу от плоскости — a < 0 и b2-4ac < 0;конус сверху от плоскости —a > 0 и b2-4ac = 0;•конус снизу от плоскости —a < 0 и b2-4ac = 0;•полуплоскость — a = 0 и b > 0;•полуплоскость — a = 0 и b < 0;•полупрямая — a = 0, b = 0 и c > 0;•полупрямая — a = 0, b = 0 и c < 0;•точка, вершина конуса — a = 0, b = 0 и c = 0.Для каждой выделенной подобласти, каждой границы (куска поверхности конуса,полуплоскости, полупрямой или точки), а также для окрестностей границ необходимоподобрать наборы значений параметров, попадающих в соответствующее множество.•abcРисунок 1.
Подобласти параметров для программы, решающей квадратные уравнения.Техники автоматизации построения тестов, нацеленных на покрытиеКак уже говорилось, методы, нацеленные на покрытие, плохо автоматизируются. Однако,есть несколько приемов, которые в ряде случаев позволяют строить такие тестыавтоматически.•Если для каждой из выделенных ситуаций написать специальную функцию,возвращающую 1, если набор значений параметров попадает в соответствующуюситуацию, и 0 иначе, можно выделять подходящие наборы значений из некоторогобольшого их множества, фильтруя его с помощью таких функций.•Для предиката, определяющего условия попадания в заданную ситуацию, можнонаписать программу на одном из языков логического программирования, например,Prolog, выполнение которой будет находить значения параметров, удовлетворяющиеэтому предикату, а значит, покрывающие соответствующую ситуацию.•Помимо Prolog’а, для решения этой задачи можно использовать другие методыпрограммирования с логическими ограничениями (Constraint Logic Programming).В ряде случае для построения наборов тестовых данных, покрывающих многоразных ситуаций, выделяемых определенным критерием покрытия, можноиспользовать генетические алгоритмы.При этом геном считается последовательность операций, выполняемых надтестируемым компонентом и данными, и заканчивающаяся вызовом некоторойоперации компонента с подготовленными данными.
Оценочной функцией всегонабора нужно считать получаемое набором тестов покрытие по выделенномукритерию, а оценочной функцией одного теста — его близость к достижению еще непокрытых ситуаций.Алгебраические методыАлгебраические методы построения тестов достаточно экзотичны и очень редкоиспользуются на практике. Основной их недостаток — необходимость иметь описаниетестируемых компонентов как абстрактных типов данных с полным набором аксиом,описывающих соотношения между операциями. Получить такие описания для практическизначимых систем очень тяжело.Эти методы дают средние значения полноты тестирования и позволяют находить ошибкиот простых до средней сложности.
Достаточно сложную и специфическую ошибку с ихпомощью найти тяжело.Один из алгебраических методов построения тестов состоит в следующем.•Выбирается некоторый набор стартовых цепочек операций.•Для каждой стартовой цепочки просматриваются все ее начала. Если одна изначальных цепочек может быть преобразована в эквивалентный вид при помощиодной из аксиом, это преобразование выполняется над всей цепочкой.В результате для каждой стартовой цепочки получается множество цепочек,эквивалентных ей. Поскольку они получены с помощью однократного примененияаксиом, их можно назвать цепочками первого порядка.Если количество полученных цепочек невелико, к каждой из них можно применитьту же технику, получив цепочки 2-го порядка, и т.д.Тестирование состоит в последовательном выполнении одной из стартовых цепочек•и всех, эквивалентных ей, со сравнением получаемых результатов.Пример.Рассмотрим алгебраическое описание списка, приведенное в Лекции 3.[].size() = 0[X.size()] ≡ [X](i <= X.size()) => X.add(i, o).size() = X.size()+1(i < X.size()) => X.remove(i).size() = X.size()–1(i < X.size()) => [X.get(i)] ≡ [X](i, j <= X.size() & i < j) => [X.add(i, o1).add(j, o2)] ≡ [X.add(j–1, o2).add(i, o1)](i <= X.size()) => [X.add(i, o1).add(i, o2)] ≡ [X.add(i, o2).add(i+1, o1)](i <= X.size()) => [X.add(i, o).remove(i)] ≡ [X](i, j <= X.size() & i < j) => [X.add(i, o).remove(j)] ≡ [X.remove(j–1).add(i, o)](i, j <= X.size() & i > j) => [X.add(i, o).remove(j)] ≡ [X.remove(j).add(i, o)](i <= X.size()) => X.add(i, o).get(i) = o(i, j <= X.size() & i < j) => X.add(i, o).get(j) = X.get(j-1)(i, j <= X.size() & i > j) => X.add(i, o).get(j) = X.get(j)(i, j < X.size()-1 & i < j) => [X.remove(i).remove(j)] ≡ [X.remove(j+1).remove(i)](i, j < X.size() & i <= j) => X.remove(i).get(j) = X.get(j+1)(i, j < X.size() & i > j) => X.remove(i).get(j) = X.get(j)В качестве стартовой возьмем одну цепочку [].add(0, o1).add(1, o2).Эквивалентными ей цепочками будут следующие.[].add(0, o1).add(1, o2).size()[].add(0, o1).add(1, o2).get(0)[].add(0, o1).add(1, o2).get(1)[].add(0, o2).add(0, o1)[].add(0, o1).add(1, o2).add(0, o3).remove(0)[].add(0, o1).add(1, o2).add(1, o3).remove(1)[].add(0, o1).add(1, o2).add(2, o3).remove(2)Цепочек второго порядка получится уже достаточно много.Чтобы проверить эквивалентность получаемых списков можно применить к ним всеоперации, возвращающие некоторые значения, и сравнить результаты.