Любая компания-разработчик, внедряющая систему автоматического тестирования своих программных продуктов, сталкивается с определенными проблемами. Возможно, опыт компании Scala Business Solutions по внедрению и сопровождению системы тестирования при разработке программ управления ресурсами предприятия, окажется интересным.

Для своевременного выявления ошибок в компании Scala Business Solutions внедрена система автоматического тестирования. Она состоит из двух частей: одна предназначена для тестирования систем Scala 5.1 и iScala 2.1 как единого целого (такое тестирование называется «интегральным»), а вторая посвящена «компонентному» тестированию, т. е. тестированию COM-компонентов.

Интегральное тестирование

В процессе разработки и сопровождения системы Scala 5.1 группа тестировщиков из 5-7 человек для каждой новой версии проверяла добавленную и существовавшую ранее функциональность. Но человек — не машина, и в ранее правильно работавшем коде время от времени появлялись ошибки. Следует учесть, что система с момента своего появления была многоязыковой; качественно провести языковое тестирование для 24 языков с помощью доступных ресурсов не представлялось возможным: представьте себе работу русскоязычного тестировщика, испытывающего программу на японском, китайском или арабском языках. В лучшем случае дело сводилось к ограниченной проверке соответствия реакций на ввод данных языковой и английской версий тестируемой системы.

Это обусловило необходимость в системе автоматического тестирования. В качестве средства реализации такой системы было решено использовать среду тестирования Rational Visual Test 6.0, включающую язык Visual Test (его синтаксис очень напоминает Visual Basic), рекордер Recorder (средство для записи скриптов — программ, воспроизводящих записанные действия пользователя), средства для организации скриптов в виде определенных структур и средства для анализа результатов тестирования. Выбор именно этой среды объяснялся тем, что в компании уже имелся опыт ее использования при тестировании производительности, вводе больших объемов данных и т.п. Однако формируемый рекордером код обладал рядом недостатков.

  1. Неустойчивость. Процесс ввода данных в тестируемую программу не был синхронизирован с готовностью программы воспринять эти данные. Записываемые скрипты не были машинно-независимыми: скрипт, записанный на быстром компьютере, не "проигрывался" на медленном. Более того, скрипты зависели и от времени выполнения: при увеличении нагрузки на сеть такие скрипты иногда невозможно было выполнить даже на том компьютере, на котором они записывались.
  2. Низкая производительность. Эта проблема, как и первая, была следствием отсутствия синхронизации. Скрипты, записанные стандартным рекордером, повторяли не только все действия, проделанные пользователем, но и его скорость ввода данных. Записанный рекордером код изобиловал командами Sleep, останавливающими выполнение скрипта на определенное время: создатели рекордера таким образом пытались решить задачу синхронизации ввода с откликом априори неизвестной программы. Плата за подобную универсальность - неустойчивость и малая производительность.
  3. Сложность поддержки записанных скриптов. Малейшее изменение интерфейса или поведения программы приводило к тому, что соответствующие изменения приходилось вносить во многие скрипты; поиск мест, где исправления необходимо было вносить, превращался в нетривиальную задачу.

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

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

Изначально мы не стремились к полной замене Visual Test. Задача состояла в том, чтобы на базе этой среды написать собственный инструментарий, предназначенный для тестирования именно системы Scala и свободный от перечисленных недостатков, так или иначе присущих любой универсальной системе автоматического тестирования.

Рис. 1. Командный файл для ввода счетов BAL1, ..., BAL100

Разработать язык для работы с тестируемой программой оказалось довольно просто. Пример кода на этом языке приведен на рис. 1. Результат проигрывания плеером помеченных команд при i = 1 изображен на рис. 2.

Рис. 2. Результат работы командного файла

Логика работы с тестируемой программой, записанная в виде скриптов, отделена от реализации соответствующих команд, за которую отвечает плеер. В реализацию команд встроена синхронизация ввода данных с готовностью тестируемой программы воспринять эти данные. Эта синхронизация происходит в цикле: перед работой с определенным элементом интерфейса с помощью функций Windows API проверяется готовность этого элемента к работе. Если элемент не готов, то плеер ждет заданный (очень малый) период времени и повторяет проверку. Если число итераций превышает некоторое заданное значение, то выполнение скрипта аварийно завершается. Одновременно с решением проблемы синхронизации увеличилась и скорость работы скриптов: затраченное на выполнение скрипта время практически равняется чистому времени, необходимому тестируемой программе для обработки введенных данных (разумеется, небольшие ресурсы компьютера тратятся на выполнение самого скрипта).

Еще одно достоинство такого подхода состоит в следующем. Знания, записанные в виде скриптов, составленных из команд, которые описывают только логику работы с тестируемой программой, не зависят от реализации интерфейса. При изменении интерфейса тестируемой программы достаточно изменить реализацию соответствующих команд, не меняя самих скриптов. В результате скрипты, записанные для Scala 5.1, удалось приспособить для тестирования системы iScala 2.1, унаследовавшей значительный объем функциональности. Для этого оказалось достаточно изменить реализацию соответствующих команд и внести незначительные изменения в самих скриптах. При этом для разных версий можно сравнивать объективные характеристики — отчеты и содержимое баз данных, не полагаясь лишь на опыт тестировщиков.

При разработке системы автоматического тестирования для iScala 2.1 пришлось отказаться от среды Visual Test. Дело в том, что iScala 2.1 поддерживает Unicode, а Visual Test — нет. В рамках выбранного нами подхода для обеспечения поддержки Unicode достаточно было переписать плеер и рекордер. Эта задача не оказалась сложной, поскольку синтаксис языка Visual Test очень похож на синтаксис Visual Basic. Для перехода на Visual Basic оказалось достаточным переписать на C++ специфические функции языка Visual Test, работающие с элементами интерфейса (нажатия на кнопки, ввод текста в текстовые поля, нажатия на пункты меню и т.д.), предусмотрев в них поддержку Unicode.

Запись скриптов

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

  1. Опираться на установку программных "ловушек" - внедряться в процедуры обработки элементов интерфейса, анализировать сообщения, приходящие этим элементам и на этом основании записывать в скрипты соответствующие команды.
  2. Включить режим записи в саму систему Scala.
  3. Имитировать интерфейс системы рекордером. При этом пользователь работает не с тестируемой программой, а с рекордером, который анализирует его действия, записывает их в скрипт и отправляет соответствующие команды на выполнение тестируемой программе.

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

Разработка рекордера оказалась одной из самых сложных задач проекта. Необходимо было:

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

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

Рис. 3. Окно рекордера

Рекордер находит активное окно, снимает его графический образ и помещает его в своем окне, располагаемом точно поверх окна тестируемой программы. Помимо этого, рекордер находит активное текстовое поле и располагает поверх него свое текстовое поле, принимающее данные, записываемые в скрипт и отправляемые после этого в тестируемую программу на выполнение. Одновременно рекордер отслеживает щелчки мышью по своему окну, содержащему графический образ (их отслеживать гораздо проще, чем сообщения, идущие в другую программу). Сопоставляя точку, где был произведен щелчок, с элементом интерфейса системы Scala, находящимся под данной точкой, рекордер «понимает», по какому элементу был произведен щелчок (на рис. 3 это могут быть поля кнопок Back и Next). В каждом случае рекордер записывает именно работу с соответствующим элементом, а не просто щелчок мышью по точке с определенными координатами. Во время записи скрипта ввод данных в тестируемую программу напрямую полностью блокируется, а все данные вводятся только через рекордер.

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

Скрипты

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

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

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

Скрипт считается выполненным успешно, если он:

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

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

Написание скрипта, выполняющегося в течение двух часов, придание ему языковой независимости и выверка соответствующих отчетов занимает не менее месяца. Как уже отмечалось, запись скрипта занимает в два-три раза больше времени по сравнению с обычной работой с системой Scala. Запись тех же действий стандартным рекордером Rational Visual Test занимает примерно столько же времени, однако получающиеся при этом скрипты необходимо еще вручную модифицировать, синхронизируя посылку данных с готовностью тестируемой программы воспринять эти данные. В итоге временные затраты на создание устойчиво работающего скрипта с помощью рекордера Rational Visual Test несопоставимы с рекордером, разработанным нами.

Решение о создании новых скриптов принимается тест-менеджером, который оценивает важность той или иной функциональности, ее стабильность и наличие ресурсов. Все скрипты записываются на английской версии тестируемой системы. Для того чтобы выполнить скрипт на каком-либо другом языке из числа поддерживаемых, этот язык необходимо указать в параметрах скрипта. Перевод зависящей от языка информации (например, пункты меню, названия окон, кнопок и ключевых слов) выполняется с помощью механизма, используемого самой системой — указанием ссылки на соответствующую строку языкового файла. К сожалению, полностью автоматизировать процесс записи ссылок на строки в языковых файлах не удалось, поскольку рекордеру не известно, зависит ли вводимая информация от языка. В частности, вводимое значение может быть ключевым словом, переводимым на другой язык (например, слово Yes, которое вводится в ответ на вопрос системы, переводится на русский язык словом «Да»), или же простым описанием тех или иных свойств обрабатываемого объекта (название счета, имя или адрес клиента и т.д.). Задача указания нужной ссылки возложена на человека, записывающего скрипт.

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

Выполнение скриптов и обработка результатов

Для хранения скриптов используется Microsoft Visual Source Safe (VSS). На первом этапе скрипты поочередно запускались для выполнения на специально выделенном сервере. Такое решение было вполне удовлетворительным до тех пор, пока общее время выполнения скриптов не превышало 24 часов. Однако по мере накопления скриптов этот порог был превышен; мы не успевали тестировать каждую из ежедневно собираемых версий. Так как большинство скриптов работает независимо друг от друга, мы попытались распараллелить их выполнение. Была создана клиент-серверная программа, позволяющая распределить выполнение скриптов между несколькими клиентскими машинами (рис. 4).

Рис. 4. Схема взаимодействия клиентской и серверной части

Поддержка и модификация скриптов

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

  • место в тестируемой программе, находясь на котором, плеер должен попытаться конвертировать выполняемый скрипт (указывается работающий в данный момент времени модуль и идентификаторы текущего диалога и элемента интерфейса);
  • требования к содержимому скрипта, при выполнении которых должна быть проведена конверсия;
  • изменения (добавляемые и удаляемые команды), которые нужно внести в ход выполнения текущего скрипта и в его новую, конвертированную версию.

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

Компонентное тестирование

Помимо интегрального подхода к автоматическому тестированию в компании Scala Business Solutions внедрено тестирование COM-компонентов, из которых строится система. Сложность тестирования таких компонентов состоит в том, что они имеют только программные интерфейсы, предназначенные для взаимодействия различных компонентов между собой. Конечно, тестировать такие компоненты можно и опосредованно, тестируя состоящую из них систему. Однако желательно иметь возможность тестировать их и по отдельности. Система iScala 2.1 состоит более чем из 700 компонентов, и строя столь масштабную конструкцию, хотелось бы быть уверенным в правильности каждой ее составной части.

Для тестирования COM-компонентов было решено написать программу, способную:

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

Такая программа, Component Auto Test (CAT) написана на Visual Basic с использованием библиотеки Tlbinf32.dll, поставляемой вместе с MS Visual Studio 6.0 и Visual Basic 6.0. Эта библиотека предоставляет доступ к информации об интерфейсах COM-классов, реализованных в той или иной динамической библиотеке DLL. Для выбранного интерфейса библиотека позволяет получить информацию о методах и свойствах соответствующего COM-объекта, параметрах и типах возвращаемых значений. Кроме того, она предоставляет средства для вызовов соответствующих методов (аналоги оператора CallByName языка Visual Basic 6.0).

Начиная работу с CAT, пользователь выбирает DLL, один из реализованных в ней COM-классов и один из интерфейсов выбранного класса. По этим данным программа создает объект, всю информацию о котором (его методы и свойства, их входные и выходные параметры и значения) можно получить во время выполнения. Анализируя полученную таким образом информацию, программа формирует пользовательский интерфейс, позволяющий задавать входные параметры и отображать выходные для выбранных методов и свойств.

Параметры типа RecordSet задаются с помощью XML-файлов; для их просмотра и редактирования предусмотрен редактор, вызываемый при нажатии на кнопку, представляющую соответствующий параметр.

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

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

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

Результаты

Для тестирования Scala 5.1 используется 40 скриптов. Время выполнения каждого скрипта — примерно полтора-два часа, причем данные вводятся на практически максимально возможной скорости. Скрипты не зависят от используемой СУБД (Microsoft SQL Server или Pervasive SQL) и от языка, на который настроена тестируемая система. Для тестирования iScala 2.1 используется 30 скриптов с таким же временем выполнения.

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

Количественные характеристики выигрыша, полученного в результате внедрения интегрального тестирования, приведены в таблице 1.

Таблица 1. Результаты внедрения интегрального тестирования

Приведем расчеты, в результате которых получаются указанные в таблице оценки. Сначала оценим время, необходимое человеку для проверки того же объема функциональности, что и проверяемого одним скриптом. По нашим оценкам, плеер вводит данные в систему Scala в 5 раз быстрее человека. Из 1,5-2 часов работы скрипта примерно 50% времени уходит на подготовку начальных данных (ввод базовой информации для скрипта). Это время, быть может, не потребуется человеку, работающему со знакомыми ему данными. Умножая оставшиеся 45 минут (нижняя оценка времени целенаправленной проверки функциональности скриптом) на 5, получаем 225 минут (примерно 4 часа), необходимых человеку на ввод некоторых данных для проверки той или иной функциональности. К этому необходимо добавить время, требуемое человеку для проверки отчетов и содержимого базы данных — объективных характеристик, подтверждающих правильность работы системы. Предположим, что человеку на это потребуется 2 часа. Скрипт эту работу выполняет автоматически. Таким образом, прогон одного скрипта экономит порядка 6 человеко-часов.

Оценим теперь время, экономящееся перед выпуском новой версии системы Scala 5.1 (это может быть как действительно новая версия, содержащая большой объем новой функциональности, так и версия, содержащая правки найденных ошибок). Для тестирования Scala 5.1 используется 40 скриптов. Эти скрипты независимо друг от друга тестируют две версии системы, работающие с Microsoft SQL Server и Pervasive SQL. Кроме того, учтем повторные прогоны для проверки исправленных ошибок, которые могли быть найдены при первом выполнении скриптов. Получившиеся 160 прогонов умножим на 6 часов, получая 960 часов, или 6 месяцев работы одного человека. Очень важно то, что автоматическими тестами эта работа выполняется не за 6 месяцев, а за 2-4 дня, непосредственно перед выпуском новой версии на рынок или перед отправкой правок клиентам.

Scala 5.1 тестируется с помощью автоматических тестов 3 года. В течение этого времени новые версии выходили примерно 6 раз в год. Существующие скрипты создавались в течение 1 года, т. е. в течение этого срока в среднем использовалось 20 скриптов. Таким образом, за первый год экономия составила 1,5 года (6 тестовых сессий по 3 человеко-месяца), а за последние 2 года экономия составляет 6 человеко-лет (2 года по 6 тестовых сессий в год по 6 месяцев экономии на одну тестовую сессию). В итоге сэкономлено 7,5 человеко-лет.

Подсчитаем теперь время, сэкономленное на языковом тестировании. По нашей оценке скорость, с которой тестер вводит информацию на языке, отличном от английского, в 15 раз меньше скорости ввода этой информации плеером. (Это очень оптимистичная для скорости ввода человеком оценка, ведь среди тестируемых языков есть такие языки, как китайский, японский и арабский.) Умножая 45 минут на 15, получаем примерно 11 часов работы человека. Умножая это время на 40 (число скриптов), на 23 (число поддерживаемых языков, отличных от английского) и на 2 прогона (второй прогон — для проверки правок ошибок, найденных при первом), получим примерно 2530 человеко-дней, или примерно 10 человеко-лет. За 3 года языковое тестирование проводилось 4 раза. Таким образом, получившаяся экономия составила 40 человеко-лет.

Таким образом, 40 скриптов, тестирующих работу системы Scala 5.1, эквивалентно работе 16 тестировщиков (47,5 человеко-лет разделенные на 3 года применения автоматических тестов). Аналогичные расчеты, проведенные для 30 скриптов, в течение полутора лет тестирующих систему iScala 2.1, дают экономию в 13 человеко-лет.

Анализ временных затрат на создание и поддержку автоматических тестов проведем на примере системы Scala 5.1, поскольку эта система прошла период разработки и сейчас находится в стабильном состоянии — в отличие от iScala 2.1, переживающей этап активной разработки. Поддержкой скриптов и поддержкой и разработкой плеера/рекордера в течение первых 1,5 лет занималось 3 человека. После этого последние 1,5 года поддержкой скриптов занимается 1 человек, тратящий на это 50% своего времени. Написание 40 скриптов для Scala 5.1 (30 из них были конвертированы в скрипты для iScala 2.1 без привлечения дополнительных ресурсов) потребовало 40 человеко-месяцев, или около 3,5 человеко-лет. Из этого времени нужно вычесть половину, так как занимающая 50% времени доводка скриптов после их записи специалистами в определенных областях функциональности осуществлялась специалистом, ответственным за поддержку скриптов, а его время мы уже учли. Следовательно, на создание этой системы было затрачено примерно 4,5 + 0,75 + 1,75 = 7 человеко-лет, а полученная экономия составляет порядка 47,5 — 7 = 40,5 человеко-лет, т.е. в 6 раз больше, чем было затрачено на разработку и сопровождение этой системы. В дальнейшем это соотношение будет только увеличиваться, поскольку система постоянно экономит ресурсы компании (3 человеко-года в год без языкового тестирования), не требуя особых затрат на разработку и сопровождение (6 человеко-месяцев в год).

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

Олег Мясниченко (Oleg.Myasnichenko@Scala.Net) — старший программист компании Scala Business Solutions N.V. (Москва).