Генерация тестов - Test generation

Генерация тестов это процесс создания набора тестовых данных или тестовых примеров для проверки адекватности новых или исправленных программные приложения. Генерация тестов считается сложной проблемой, и, хотя появилось множество решений, большинство из них ограничиваются игрушечными программами. Генерация тестов - один из аспектов тестирование программного обеспечения.Поскольку тестирование является трудоемким и составляет почти треть стоимости разработки системы, проблема быстрого, эффективного и точного создания качественных тестовых данных считается важной.[1]

Базовые концепты

Пример диаграммы потока управления

Математическое моделирование

Программа п можно рассматривать как функцию P: S → R, где S это набор всех возможных входов и р набор всех возможных выходов. Входная переменная функции P отображается на входной параметр P. P (x) обозначает выполнение программы для определенного входа x.[1][2]

График потока управления

А График потока управления программы п - ориентированный граф G = (N, E, s, e), состоящий из набора узлов N и набора ребер E = {(n, m) | n, m ∈ N}, соединяющих узлы.[1][2]

Каждый узел обозначает базовый блок, который представляет собой последовательность инструкций. Важно отметить, что в каждый базовый блок элемент управления входит через узел входа и уходит в конце без остановки или ветвления, за исключением конца. По сути, блок всегда выполняется целиком. Узлы входа и выхода - это два специальных узла, обозначенных s и e соответственно.

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

Модель

Генератор тестовых данных модели

Генератор тестовых данных выполняет следующие шаги

  1. Построение графа управления программой
  2. Выбор пути
  3. Создание тестовых данных

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

  1. Найдите предикат пути для пути
  2. Решите предикат пути

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

Генераторы тестовых данных

На основе Математическое моделирование выше мы можем просто сформулировать проблему генератора тестовых данных как: Для данной программы п и путь ты, сгенерировать вход x ∈ S, так что Икс проходит путь ты.

Генераторы случайных тестовых данных

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

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

пустота тест(char Икс, char у) {    если (Икс == у)        printf("Равный");    еще        printf("Не равный");}

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

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

Генераторы целевых тестовых данных

Целенаправленный подход предлагает руководство к определенному набору путей. Генераторы тестовых данных в этом подходе генерируют входные данные для любого пути u вместо обычного подхода генерации входных данных от входа до выхода блока кода. Таким образом, генератор может найти любой вход для любого пути п который является подмножеством пути ты. Это резко снижает риск создания относительно невозможных путей и дает возможность направить поиск. Два метода следуют этой технике:

  1. Цепной подход
  2. Утверждение-ориентированный подход.

Цепной подход

Цепной подход является продолжением целевого подхода. Видно, что основным ограничением методов генерации тестовых данных является то, что для генерации тестовых данных используется только граф потока управления. Эти ограниченные знания могут затруднить наш выбор. Таким образом, видно, что ориентированный на путь подход обычно должен генерировать большое количество путей, прежде чем он найдет «правильный» путь. Это потому, что выбор пути слепой.[4] Подход с цепочкой пытается идентифицировать цепочку узлов, которые жизненно важны для выполнения целевого узла. Цепной подход начинается с выполнения для любого произвольного входа x. Программа поиска во время выполнения каждой ветви решает, продолжать ли выполнение через эту ветвь или выбрать альтернативную ветвь, потому что текущая ветвь не ведет к целевому узлу. Если замечено, что поток выполнения нежелателен, тогда используются алгоритмы поиска для автоматического поиска нового ввода, чтобы изменить выполнение потока. Однако, если для этой точки также процесс поиска не может найти вход X для изменения потока выполнения, тогда подход с цепочкой пытается изменить поток в узле p, благодаря чему может быть выполнено альтернативное ветвление в p.[4][5]

Утверждение-ориентированный подход

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

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

пустота тест(int а) {    int б, c    б = а-1;    утверждение(б != 0);    c = (1/б);}

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

Генераторы данных путевого тестирования

Постатейная генерация тестовых данных считается одним из лучших подходов к генерации тестовых данных. Этот подход не дает генератору возможности выбора между несколькими путями, а просто дает ему один конкретный путь для работы. Отсюда и название «Генератор данных путевого тестирования». Таким образом, за исключением того факта, что этот метод использует определенные пути, он очень похож на создание тестовых данных, ориентированных на достижение цели. Использование конкретных путей приводит к лучшему знанию и предсказанию покрытие. Однако это также затрудняет создание необходимых тестовых данных.

Генераторы данных путевого тестирования требуют от пользователя двух входных данных:

  1. Программа для тестирования
  2. Критерий тестирования (например: покрытие пути, покрытие оператора и т. Д.)

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

Генераторы интеллектуальных тестовых данных

Интеллектуальные генераторы тестовых данных зависят от сложного анализа кода, который направляет поиск тестовых данных. Интеллектуальные генераторы тестовых данных по существу используют один из методов генерации тестовых данных в сочетании с подробным анализом кода. Этот подход может генерировать тестовые данные быстрее, чем другие подходы, но анализ, необходимый для использования этого подхода в широком спектре программ, довольно сложен и требует большого понимания, чтобы предвидеть различные ситуации, которые могут возникнуть.[7][8] Для этого есть пакеты с открытым исходным кодом, такие как DataGenerator.[9]

Генераторы хаотических данных

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

Генераторы гипермедиа

Генераторы гипермедиа генерируют гипертекст, гиперт тома и гиперфильмы.

Генераторы квантовых данных

Генератор квантовых данных генерирует кубиты в соответствии с каким-либо другим генератором данных. В данном случае кубиты - это данные.

Генераторы утверждений теста

Когда у нас есть некоторые тестовые данные, нам понадобится тестовый оракул для оценки поведения тестируемой программы для заданного тестового ввода. Можно использовать разные типы оракулов.[11]

Также можно улучшить утверждения для существующих тестовых данных. Например, можно сгенерировать и добавить новые утверждения в существующие тестовые примеры. Это то, что делает система DSpot в контексте языка программирования Java: она выполняет динамический анализ тестовых примеров JUnit и генерирует недостающие утверждения.[12]

Генераторы тестовых случаев

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

// Пример сгенерированного тестового примера, который охватывает конструктор и два метода.@Тестпустота createdJunitTest() {    Счет б = новый Банковский счет(0);    б.депозит(7);    б.снять со счета(3);}

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

@Тестпустота createdJunitTest() {    Счет б = новый Банковский счет(0);    б.депозит(7);    б.снять со счета(3);    assertEquals(4, б.getAmount());}

EvoSuite является примером такого генератора тестовых примеров для Java.

Проблемы генерации тестов

Генерация тестов очень сложна. Использование динамического распределения памяти в большей части кода, написанного в промышленности, является наиболее серьезной проблемой, с которой сталкиваются генераторы тестовых данных, поскольку использование программного обеспечения в этом случае становится крайне непредсказуемым, из-за чего становится труднее предвидеть пути, по которым программа может сделать почти невозможным создание генераторов тестовых данных исчерпывающих тестовых данных. Однако за последнее десятилетие был достигнут значительный прогресс в улучшении решения этой проблемы за счет использования генетических алгоритмов и других алгоритмов анализа. Ниже перечислены проблемные области, которые встречаются при реализации методов генерации тестовых данных для реального кода, используемого в отрасли.[2]

Массивы и указатели

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

Объекты

Объекты из-за своего динамического характера представляют проблему для поколения. Это дополнительно усугубляется использованием других объектно-ориентированные функции. Все это затрудняет определение того, какой код будет вызываться во время выполнения. Была сделана попытка решить проблему объектно-ориентированного кода с помощью мутации.[13]

Петли

Циклы, которые изменяют свое поведение в зависимости от входных переменных, потенциально проблематичны, поскольку трудно предвидеть путь, по которому можно было бы пойти. Однако, если данный путь специфичен, то есть он не меняет поведения, петли не вызывают проблем. Есть несколько методов, которые были предложены для решения этой потенциальной проблемы с петлями.[14]

Модули

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

  • Решение грубой силы Это делается путем встраивания вызываемых функций в целевой
  • Анализ вызываемых функций Сначала проанализируйте вызываемые функции и сгенерируйте предикаты пути для этих функций.

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

Невозможные пути

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

Удовлетворение ограничений

Удовлетворение ограничений как следует из названия, это процесс поиска решения, которое соответствует набору ограничений, которым должны удовлетворять переменные. Таким образом, решение - это вектор переменных, удовлетворяющий всем ограничениям. Удовлетворение ограничений - трудная проблема для решения и, следовательно, обычно не выполняется должным образом. Все программы так или иначе должны удовлетворять тому или иному ограничению. Было много таких методов, как итеративная релаксация, генетические алгоритмы и т.д., которые позволяют решать ограничения.[6][7]

Читаемость сгенерированных тестов

Одна из проблем генерации тестов - удобочитаемость: когда сгенерированные тесты предназначены для включения в набор тестов, разработчики могут легко понять сгенерированные тестовые примеры. Однако сгенерированные тестовые примеры часто содержат непонятные имена сгенерированных переменных. [15] Один из способов преодоления этой проблемы состоит в том, что вместо создания новых тестов для улучшения существующих тестов, уже написанных людьми, это называется амплификацией тестов.[16]

Смотрите также

Рекомендации

  1. ^ а б c Корель, Богдан (август 1990). «Автоматизированная генерация тестовых данных программного обеспечения». IEEE Transactions по разработке программного обеспечения. 16 (8): 870–879. CiteSeerX  10.1.1.121.8803. Дои:10.1109/32.57624.
  2. ^ а б c d Эдвардссон, Джон (октябрь 1999 г.). «Обзор автоматического создания тестовых данных». Труды Второй конференции по информатике и инженерии в Линчёпинге. CiteSeerX  10.1.1.20.963.
  3. ^ Offutt, J .; Дж. Хейс (1996). «Семантическая модель ошибок программы». Международный симпозиум по тестированию и анализу программного обеспечения. CiteSeerX  10.1.1.134.9338.
  4. ^ а б Корель, Богдан (1990). «Динамический подход к автоматизированной генерации тестовых данных». Конференция по сопровождению программного обеспечения.
  5. ^ Фергюсон, Роджер; Богдан Корель (1996). «Цепной подход для создания тестовых данных программного обеспечения» (PDF). ACM.
  6. ^ а б DeMillo, R.A .; Оффутт А.Дж. (Сентябрь 1991 г.). «Автоматическая генерация тестовых данных на основе ограничений». IEEE Transactions по разработке программного обеспечения. 19 (6): 640. CiteSeerX  10.1.1.91.1702. Дои:10.1109/32.232028.
  7. ^ а б Паргас, Рой; Харролд, Мэри; Пек, Роберт (1999). «Генерация тестовых данных с использованием генетических алгоритмов» (PDF). Журнал тестирования, проверки и надежности программного обеспечения. 9 (4): 263–282. CiteSeerX  10.1.1.33.7219. Дои:10.1002 / (sici) 1099-1689 (199912) 9: 4 <263 :: aid-stvr190> 3.0.co; 2 года.
  8. ^ Michael, C.C .; McGraw, G.E .; Schatz, M.A .; Уолтон, К. (1997). «Генетические алгоритмы генерации данных динамических тестов». Труды 12-й Международной конференции IEEE Автоматизированная разработка программного обеспечения. С. 307–308. CiteSeerX  10.1.1.50.3866. Дои:10.1109 / ASE.1997.632858. ISBN  978-0-8186-7961-2.
  9. ^ «Генератор данных». finraos.github.io. Получено 2019-09-01.
  10. ^ «Генератор данных хаоса».
  11. ^ Барр, Эрл Т .; Харман, Марк; Макминн, Фил; Шахбаз, Музаммил; Ю, Шин (2015-05-01). «Проблема Oracle в тестировании программного обеспечения: обзор». IEEE Transactions по разработке программного обеспечения. 41 (5): 507–525. Дои:10.1109 / TSE.2014.2372785. ISSN  0098-5589.
  12. ^ Данглот, Бенджамин; Вера-Перес, Оскар Луис; Бодри, Бенуа; Монперрус, Мартин (2019). «Улучшение автоматического тестирования с DSpot: исследование с десятью зрелыми проектами с открытым исходным кодом». Эмпирическая разработка программного обеспечения. 24 (4): 2603–2635. arXiv:1811.08330. Дои:10.1007 / s10664-019-09692-у. ISSN  1573-7616.
  13. ^ Сеатер, Роберт; Грегори Деннис. «Автоматизированное создание тестовых данных с помощью SAT» (PDF). Цитировать журнал требует | журнал = (помощь)
  14. ^ а б Ramamoorthy, C.V .; С. Ф. Хо; В. Т. Чен (декабрь 1976 г.). «Об автоматизированном формировании тестовых данных программ». IEEE Transactions по разработке программного обеспечения. SE-2 (4): 293–300. Дои:10.1109 / цэ.1976.233835.
  15. ^ Грано, Джованни; Скалабрино, Симона; Gall, Harald C .; Оливето, Рокко (2018). «Эмпирическое исследование читабельности ручных и сгенерированных тестовых случаев» (PDF). Материалы 26-й конференции по пониманию программ - ICPC '18. С. 348–351. Дои:10.1145/3196321.3196363. ISBN  9781450357142.
  16. ^ Данглот, Бенджамин; Вера-Перес, Оскар; Юй Чжунсин; Зайдман, Энди; Монперрус, Мартин; Бодри, Бенуа (2019). «Растущее как снежный ком исследование литературы по усилению тестов». Журнал систем и программного обеспечения. 157: 110398. arXiv:1705.10692. Дои:10.1016 / j.jss.2019.110398.