Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Конференция «Технологии управления данными 2018»
СУБД, платформы, инструменты, реальные проекты.
29 ноября 2018 г.
2008 г.

Опыт применения технологии Azov для тестирования библиотеки Qt3

Р. С. Зыбин

Назад Содержание

3. Дополнительное обеспечение корректности тестового набора

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

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

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

3.1. Проверки, добавляемые автоматически

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

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

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

Более конкретные, и потому возможно более полезные, требования задаются разработчиками для каждого типа данных в отдельности и оформляются в виде так называемых общих специализированных типов. В качестве примера таких требований можно привести проверку свойств isNull() и isValid(), которые имеются у большого числа классов и характеризуют полноценность объекта.

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

3.2. Контроль корректности наложенных ограничений

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

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

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

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

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

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

4. Оптимизация разработки тестов

Ключевым достоинством технологии Azov является возможность достичь чрезвычайно высокой производительности при разработке тестового набора. Затраты на создание одного теста уменьшаются при разработке тестов для интерфейсов большого размера (свыше 1000 операций). Основные трудозатраты приходятся на задачи, выполняемые разработчиком вручную, а именно изучение стандарта, формирование необходимых специализированных типов и отладка полученных тестов.

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

4.1. Приведение типов и наследование

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

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

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

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

Специализированные типы для производных типов, таких как указатели, ссылки, синонимы (typedef), константы (const), а также конструкции более сложной структуры, можно создавать автоматически с помощью механизма неявного наследования. Производные одного типа образуют класс эквивалентности в том смысле, что ограничения специализированного типа для одного из них могут быть применены к любому другому типу из этого множества. Дополнительные поправки в цепочку инициализации параметра автоматически вносятся компоновщиком при сборке теста. С точки зрения пользователя системы это выглядит так, как будто для всех параметров, имеющих типы из одного класса эквивалентности, существуют специализированные типы одинакового назначения.

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

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

4.2. Удобство и функциональность интерфейса

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

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

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

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

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

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

4.3. Разбиение на группы и порядок выполнения работ

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

Для того чтобы минимизировать время на переключение контекста в процессе разработки, используется инструмент построения расписания работ. Множество операций, для которых создается тестовый набор, разбивается на группы в соответствии с их назначением, что позволяет разработчикам изучать один и тот же раздел стандарта сразу для целой группы функций или классов. Затем для каждой группы и для каждой операции внутри группы определяется приоритет, согласно которому следует вести разработку. Применительно к C++ удобнее оперировать не в терминах отдельных операций, а в терминах целых классов.

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

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

5. Достоинства и недостатки подхода

Описанная в предыдущих разделах реализация системы разработки тестов, основанная на технологии Azov, была применена при создании тестового набора для интерфейса библиотеки Qt3. Тестированию подлежало около 10000 операций, поведение которых описывается стандартом LSB. При этом практически во всех случаях удалось построить работоспособные простейшие тесты. На основе полученного опыта можно сделать выводы о преимуществах и недостатках технологии в целом и данной ее реализации.

Технология построения простейших тестов работоспособности действительно позволяет создавать тестовые наборы, решающие поставленную задачу для интерфейсов большого размера. Эффективность ее применения возрастает в тех случаях, когда интерфейс содержит небольшое количество существенно различных типов данных по сравнению с общим числом операций в нем. Это можно увидеть на примере подсистемы работы с базами данных QSql, которая содержит 447 операций из LSB (501 всего), 20 собственных классов и 7 общеупотребительных типов данных. При этом работающие тесты можно построить даже при отсутствии документации на часть операций, что для библиотеки Qt более чем актуально.

В результате расширения базовой методики технологии Azov простейшие тесты удалось создать для приблизительно 99,5% объема входящей в LSB части интерфейса Qt. Лишь около 35 операций не получилось протестировать по причине простоты тестов или критической нехватки документации. При этом в процессе описания ошибок проявилась неожиданная сложность. Дело в том, что при отсутствии сколько-нибудь подробной документации чрезвычайно трудно отличить случаи неверного использования операции от случаев обнаружения настоящих ошибок в ее реализации. Фактически для принятия решения в этих случаях приходится изучать исходный код операций.

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

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

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

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

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

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

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

6. Заключение

В статье описываются техники, позволяющие адаптировать общий подход технологии Azov для создания тестов работоспособности для классов языка C++ и библиотеки Qt3. Большинство из таких техник могут быть использованы и в других системах.

На их основе была реализована система генерации тестов, позволившая с приемлемыми усилиями построить тестовый набор для 10000 методов и функций из библиотеки Qt3, описанных в стандарте LSB. При этом, несмотря на неполноту документации, производительность разработки тестов составила в среднем 70 операций в день на человека.

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

Таким образом, показана применимость на практике и высокая эффективность технологии Azov, пополненной набором описанных в данной работе техник.

Литература

  1. Р. Зыбин, В. Кулямин, А. Пономаренко, В. Рубанов, Е. Чернов. Технология Azov автоматизации массового создания тестов работоспособности. Опубликовано в этом же сборнике.
  2. http://www.linuxbase.org.
  3. LSB Navigator.
  4. Qt Reference Documentation (Open Source Edition).
  5. А. Пономаренко, Е. Чернов. Алгоритм генерации тестов работоспособности на основе расширенной базы данных LSB. Опубликовано в этом же сборнике.

Назад Содержание

Новости мира IT:

Архив новостей

Последние комментарии:

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 985 1945361
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2015 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...