В этом посте я опишу свои исследования, которые мне пришлось проделать, когда на проекте стал вопрос об автоматизации тестирования. Проект представляет собой веб-сайт, клиентская часть которого написана на Flex, а серверная — на Python(Django).
Для начала рассмотрим путь от начала, который я прошел, прежде чем остановился на связке Java + Selenium + FlexMonkium. А это:
Первым, что я попытался использовать в качестве средств автоматизации, был Sfapi(Selenium Flex-API). Работает он в связке с Selenium RC и каким-либо языком программирования, для которого есть драйвера (Java, C#, Python, Ruby). Из преимуществ следует отметить, что это бесплатное решение, которое действительно работает. Из недостатков — много кода, но мало результата. Плохо работает с динамическими элементами приложения. Причиной дальнейшего поиска решений стали большие объемы кода, которые выполняли простенькие операции.
Следующим софтом для автоматизации Flex-приложений, который я попробовал, оказался Ranorex Studio. Это очень приятный в использовании программный комплекс, с интуитивно понятным интерфейсом. Из плюсов стоит отметить быстрое создание тестов без знания языка программирования с помощью встроенного функционала и Spy-агента (который записывает последовательность действий пользователя), допиливание уже созданных тестов, методом конвертации их в любой язык программирования из предлагаемых (список поддерживаемых языков зависит от типа купленной лицензии) и хорошее взаимодействие с практически всеми UI-элементами Flex (на формах встречал жалобы на некорректную работу с элементом DataGrid, но во время работы мне не довелось с ним поработать). Из недостатков — высокая стоимость лицензии, если нужно тестировать приложение на разных доменах (например, тестовый и главный сервера), то необходимо предварительно с тестами произвести кое-какие преобразования, что бы они корректно начали взаимодействовать с интерфейсом приложения. Именно высокая стоимость лицензии послужила причиной для дальнейшего поиска решений.
Не дожидаясь окончания лицензии Ranorex, я продолжил поиски в сторону свободного ПО и на одном из форумов наткнулся на совет использовать FlexMonkey. На их сайте есть коротенькая, но вполне понятная документация, так же имеется форум с активными обсуждениями и «живая» поддержка. Само приложение написано на Adobe Air, имеет понятный и удобный интерфейс.
Как оказалось, FlexMonkey очень шустрый, удобный и бесплатный. Корректно поддерживает работу практически со всеми UI-компонентами (по крайне мере у меня работало все на ура), но так же имеет один недостаток — тесты нельзя усовершенствовать с помощью какого-либо языка программирования. По сути можно пользоваться только базовыми функциями, такими как проверка свойств компонентов, сравнивание значений этих свойств и т.д. Но, например, сделать подсчет писем в почтовом интерфейсе пользователя без средств программирования не получится. Вот здесь на помощь приходит следующий продукт от создателей FlexMonkey, который работает в связке с Selenium и называется FlexMonkium.
Для того, что бы приступить к автоматизации тестирования своего flex-приложения, необходимо настроить проект на взаимодействие с Selenium RC + FlexMonkium, а так же подготовить среду программирования (для примера я выбрал язык Java с Eclipse IDE). Далее все действия расписаны по шагам:
1. Скачиваем Selenium RC и Selenium Client Driver для языка программирования, на котором будут писаться тесты (в моем случае Java).
2. Скачиваем FlexMonkium (в примере использовалась версия 4.1.8). В архиве находятся три компонента — папка с библиотеками .swc, одну из которых нужно будет подключить к Flex-проекту, «user-extensions.js», являющийся расширением для Selenium server, благодаря которому он «будет понимать» команды управления flex-приложением, указанные в тестах и .xpi плагин, расширяющий функционал Selenium IDE для Firefox.
3. Открываем Flex-проект во Flash Builder и подключаем к проекту библиотеку .swc из скачанного архива. Всего в архиве несколько таких файлов с одинаковым названием и цифрой в конце. Цифра означает номер версии Flex-SDK (в моем случае это четвертая версия, то есть нужно подключить automation_monkey4.x.swc). После этого перекомпилируем проект и вуаля — он готов взаимодействовать с Selenium сервером.
4. Теперь нужно запустить Selenium сервер с файлом расширения команд, который позволит ему «понимать» команды, специфичные для управления flex-приложением. Этот файл содержится в скачанном архиве FlexMonkium и называется «user-extensions.js». Передается в качестве параметра в командной строке при запуске сервера. Для этих целей я создал отдельный bat-файл со следующим содержанием:
После этого можно смело запускать сервер. Если все было правильно сделано, после запуска bat'ника на экране будет висеть примерно такое окошко:
Итак, тестируемое приложение готово к взаимодействию со средствами автоматизации тестирования, а Selenium-RC запущен и понимает команды управления flex-компонентами.
В качестве языка для написания тестов я выбрал Java со средой разработки Eclipse в связке с библиотекой JUnit. При таком сочетании тесты прекрасно работали, но JUnit после каждого теста перезапускал браузер, что для меня было не желательно, так как многие мои тесты были продолжением предыдущих, а сброс состояния приложения меня не устраивал. После некоторых поисков по форумам я прочитал, что такое поведение невозможно отключить и мне пришлось отказаться от использования JUnit. В отчете мне требовалось всего лишь знать, какие тесты не прошли, на каких шагах это случилось и сколько всего таких ошибок возникло. Для этих целей я написал специальную функцию, которая выводит в консоль данную информацию. Она вызывается в случае, когда тест не прошел (когда не проходит тест, у него срабатывает таймаут) и Java сгенерировал исключение. В итоге у меня получился такой код:
В данном примере тестируется отправка письма по внутренней почте приложения, а так же проверка его отображения среди исходящих писем.
А теперь подробнее. Функции «seleniumStart» и «seleniumStop» начинают и завершают сессию. В первой функции инициализируется экземпляр класса Selenium, который посылает команды серверу. В него передаются такие параметры, как хост и порт (на котором запущен сервер Selenium), браузер (в котором будет проводиться тестирование) и url (по которому доступно тестируемое приложение).
Далее идет функция «printError», которая принимает в качестве параметров название теста и номер шага и выводит их в консоль. Вызывается эта функция при достижении таймаута на каком-либо шаге теста.
Чтобы не писать в каждом шаге блок повторяющегося кода, он был вынесен в отдельную функцию, называемую «testExec». Ее параметрами являются название и номер выполняемого шага (которые передаются в «printError» по истечению таймаута), команда для сервера, путь (локатор) к элементу в приложении, до которого пытаемся «достучаться», и таймаут. Обратите внимание, что путь приложения в приведенном примере имеет 2 типа: «VerifyProperty» и «UIEvent ». Первый используется для получения\сравнения значений атрибутов какого-либо элемента, а второй для осуществления действия над указанным элементом, например, кликнуть на кнопке. Указать можно UI-элемент приложения не только по его имени или id, но и по совокупности нескольких его свойств, которые дополнительно указываются в тегах «arg». Более подробную документацию можно получить на сайте Gorilla Logic.
Чтобы не писать локаторы вручную, я записывал последовательность шагов через FlexMonkey, экспортировал тесты в xml файл и вставлял в Java-код. В результате получался такой результат:
Или же можно установить плагин flexmonkium.xpi в дополнении к Selenium IDE и делать запись тестов непосредственно из браузера Firefox.
Но во всяком случае эти 2 способа не гарантируют 100% точной работы тестов, особенно когда компоненты приложения имеют динамически генерируемый ID. В этом случае придется вручную дописать дополнительные аргументы, явно присущие требуемому компоненту. Свойства компонентов можно посмотреть во FlexMonkey или FlexMonkium Console.
На данный момент на проекте автоматизировано большинство регрессионных тестов. Их количество составило > 350, время выполнения которых примерно 3-4 часа.
Прошу прощения за такой большой кусок кода в примере, который раздул объем статьи.
Выбор средств для автоматизации
Для начала рассмотрим путь от начала, который я прошел, прежде чем остановился на связке Java + Selenium + FlexMonkium. А это:
- Selenium + Sfapi
- Ranorex
- FlexMonkey
- Selenium + FlexMonkium
Первым, что я попытался использовать в качестве средств автоматизации, был Sfapi(Selenium Flex-API). Работает он в связке с Selenium RC и каким-либо языком программирования, для которого есть драйвера (Java, C#, Python, Ruby). Из преимуществ следует отметить, что это бесплатное решение, которое действительно работает. Из недостатков — много кода, но мало результата. Плохо работает с динамическими элементами приложения. Причиной дальнейшего поиска решений стали большие объемы кода, которые выполняли простенькие операции.
Следующим софтом для автоматизации Flex-приложений, который я попробовал, оказался Ranorex Studio. Это очень приятный в использовании программный комплекс, с интуитивно понятным интерфейсом. Из плюсов стоит отметить быстрое создание тестов без знания языка программирования с помощью встроенного функционала и Spy-агента (который записывает последовательность действий пользователя), допиливание уже созданных тестов, методом конвертации их в любой язык программирования из предлагаемых (список поддерживаемых языков зависит от типа купленной лицензии) и хорошее взаимодействие с практически всеми UI-элементами Flex (на формах встречал жалобы на некорректную работу с элементом DataGrid, но во время работы мне не довелось с ним поработать). Из недостатков — высокая стоимость лицензии, если нужно тестировать приложение на разных доменах (например, тестовый и главный сервера), то необходимо предварительно с тестами произвести кое-какие преобразования, что бы они корректно начали взаимодействовать с интерфейсом приложения. Именно высокая стоимость лицензии послужила причиной для дальнейшего поиска решений.
Не дожидаясь окончания лицензии Ranorex, я продолжил поиски в сторону свободного ПО и на одном из форумов наткнулся на совет использовать FlexMonkey. На их сайте есть коротенькая, но вполне понятная документация, так же имеется форум с активными обсуждениями и «живая» поддержка. Само приложение написано на Adobe Air, имеет понятный и удобный интерфейс.
Как оказалось, FlexMonkey очень шустрый, удобный и бесплатный. Корректно поддерживает работу практически со всеми UI-компонентами (по крайне мере у меня работало все на ура), но так же имеет один недостаток — тесты нельзя усовершенствовать с помощью какого-либо языка программирования. По сути можно пользоваться только базовыми функциями, такими как проверка свойств компонентов, сравнивание значений этих свойств и т.д. Но, например, сделать подсчет писем в почтовом интерфейсе пользователя без средств программирования не получится. Вот здесь на помощь приходит следующий продукт от создателей FlexMonkey, который работает в связке с Selenium и называется FlexMonkium.
Настройка рабочей среды
Для того, что бы приступить к автоматизации тестирования своего flex-приложения, необходимо настроить проект на взаимодействие с Selenium RC + FlexMonkium, а так же подготовить среду программирования (для примера я выбрал язык Java с Eclipse IDE). Далее все действия расписаны по шагам:
1. Скачиваем Selenium RC и Selenium Client Driver для языка программирования, на котором будут писаться тесты (в моем случае Java).
2. Скачиваем FlexMonkium (в примере использовалась версия 4.1.8). В архиве находятся три компонента — папка с библиотеками .swc, одну из которых нужно будет подключить к Flex-проекту, «user-extensions.js», являющийся расширением для Selenium server, благодаря которому он «будет понимать» команды управления flex-приложением, указанные в тестах и .xpi плагин, расширяющий функционал Selenium IDE для Firefox.
3. Открываем Flex-проект во Flash Builder и подключаем к проекту библиотеку .swc из скачанного архива. Всего в архиве несколько таких файлов с одинаковым названием и цифрой в конце. Цифра означает номер версии Flex-SDK (в моем случае это четвертая версия, то есть нужно подключить automation_monkey4.x.swc). После этого перекомпилируем проект и вуаля — он готов взаимодействовать с Selenium сервером.
4. Теперь нужно запустить Selenium сервер с файлом расширения команд, который позволит ему «понимать» команды, специфичные для управления flex-приложением. Этот файл содержится в скачанном архиве FlexMonkium и называется «user-extensions.js». Передается в качестве параметра в командной строке при запуске сервера. Для этих целей я создал отдельный bat-файл со следующим содержанием:
java -jar C:\Automation\selenium-server-standalone-2.0rc2.jar -userExtensions C:\user-extensions.js
После этого можно смело запускать сервер. Если все было правильно сделано, после запуска bat'ника на экране будет висеть примерно такое окошко:
Итак, тестируемое приложение готово к взаимодействию со средствами автоматизации тестирования, а Selenium-RC запущен и понимает команды управления flex-компонентами.
Создаем первый тест
В качестве языка для написания тестов я выбрал Java со средой разработки Eclipse в связке с библиотекой JUnit. При таком сочетании тесты прекрасно работали, но JUnit после каждого теста перезапускал браузер, что для меня было не желательно, так как многие мои тесты были продолжением предыдущих, а сброс состояния приложения меня не устраивал. После некоторых поисков по форумам я прочитал, что такое поведение невозможно отключить и мне пришлось отказаться от использования JUnit. В отчете мне требовалось всего лишь знать, какие тесты не прошли, на каких шагах это случилось и сколько всего таких ошибок возникло. Для этих целей я написал специальную функцию, которая выводит в консоль данную информацию. Она вызывается в случае, когда тест не прошел (когда не проходит тест, у него срабатывает таймаут) и Java сгенерировал исключение. В итоге у меня получился такой код:
import java.awt.event.KeyEvent;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.HttpCommandProcessor;
import com.thoughtworks.selenium.Selenium;
public class TestRunner {
private Selenium selenium;
private HttpCommandProcessor proc;
private int errorsFound;
//Генерируем уникальный текст письма что бы не путаться
DateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_ss");
Date date = new Date();
String msg_text = dateFormat.format(date);
public static void main(String[] args) {
TestRunner tr= new TestRunner();
tr.errorsFound = 0;
try{
tr.seleniumStart();
//----Тестируем почту----
tr.goMail();
tr.postSend();
tr.outBoxVerifyMessage();
}catch(Exception ex) {}
}
//Starting selenium server
public void seleniumStart() throws InterruptedException
{
proc = new HttpCommandProcessor("localhost", 4444,
"*firefox3 c:\\Program Files\\MozillaFirefox3.6\\firefox.exe", "http://q/");
selenium = new DefaultSelenium(proc);
selenium.start();
Thread.sleep(5000);
selenium.open("/site_media/");
}
//Shutting down selenium server
public void seleniumStop()
{
if (selenium != null) {
selenium.stop();
selenium = null;
}
System.out.println("\r\nTesting compleated. Errors found: "+errorsFound);
}
//Error reporter
public void printError(String test, String step)
{
errorsFound ++;
System.out.println("\r\nError in Test: '"+test+"' on step: '"+step+"'");
}
//Step runner
public void testExec(String testName, String step, String cmd, String locator,
int time) throws Exception
{
for (int t = 0;; t++) {
if (t >= time) printError(testName, "Step "+step);
try {
if (proc.getBoolean(cmd, new String[] {locator})) break;
} catch (Exception e) { }
Thread.sleep(500);
}
}
//-----------------------------------------------------------------------------------
////////////////////////////////////////ПОЧТА////////////////////////////////////////
//-----------------------------------------------------------------------------------
//Переходим на страницу почты
public void goMail() throws Exception {
String test = "goMail";
//Step 1. Ждем загрузки флешки
testExec(test, "1", "isFlexMonkey", "", 60);
//Step 2. Кликаем на кнопку "Почта"
testExec(test, "2", "isFlexMonkey",
"<UIEvent command=\"Select\" value=\"tabBar\"><arg value=\"Почта\"/> </UIEvent>", 60);
}
//-----------------------------------------------------------------------------------
//Проверка отсылки почты
public void postSend() throws Exception {
String test = "postSend";
//Step 3. Кликаем на кнопку "Написать"
testExec(test, "3", "isFlexMonkey",
"<UIEvent command=\"Select\" value=\"buttonBarMail\"><arg value=\"Написать\"/></UIEvent>", 60);
//Step 4. Раскрываем список адресатов
testExec(test, "4", "isFlexMonkey",
"<UIEvent command=\"Open\" value=\"userDataProvider\"><arg value=\"null\"/></UIEvent>", 60);
//Step 5. Выбираем адресата из списка
testExec(test, "5", "isFlexMonkey",
"<UIEvent command=\"Select\" value=\"userDataProvider\"><arg value=\"Василий Пупкин\"/></UIEvent>", 60);
//Step 6. Переходим в поле "Тема"
testExec(test, "6", "isFlexMonkey",
"<UIEvent command=\"SelectText\" value=\"subject\"><arg value=\"0\"/><arg value=\"0\"/></UIEvent>", 60);
//Step 7. Пишем тему письма
testExec(test, "7", "isFlexMonkey",
"<UIEvent command=\"Input\" value=\"subject\"><arg value=\"Hello, "+msg_text+"\"/></UIEvent>", 60);
//Step 8. Переходим в следующее поле (текст письма)
testExec(test, "8", "isFlexMonkey", "<UIEvent command=\"ChangeFocus\" value=\"subject\"/>", 60);
//Step 9. Пишем текст письма
testExec(test, "9", "isFlexMonkey",
"<UIEvent command=\"Input\" value=\"message\"><arg value=\"Test of text message "
+msg_text+"\"/></UIEvent>", 60);
//Step 10. Жмем кнопку отправить
testExec(test, "10", "isFlexMonkey", "<UIEvent command=\"Click\" value=\"Отправить\"/>", 60);
}
//-----------------------------------------------------------------------------------------------
//Проверка отправленного письма в исходящих
public void outBoxVerifyMessage() throws Exception {
String test = "outBoxVerifyMessage";
//Step 1. Переходим в исходящие
testExec(test, "1", "isFlexMonkey",
"<UIEvent command=\"Select\" value=\"buttonBarMail\"><arg value=\"Исходящие\"/></UIEvent>", 60);
//Step 2. Проверяем тему
testExec(test, "2", "isFlexMonkey",
"<VerifyProperty value=\"subject\" propertyString=\"text\" expectedValue=\"Hello, "+msg_text+"\"/>", 10);
//Step 3. Проверяем текст
testExec(test, "3", "isFlexMonkey",
"<VerifyProperty value=\"message\" propertyString=\"text\" expectedValue=\"Test of text message "
+msg_text+"\"/>", 10);
//Step 4. Проверяем получателя
testExec(test, "4", "isFlexMonkey",
"<VerifyProperty value=\"fullName\" propertyString=\"text\" expectedValue=\"Василий Пупкин\"/>", 10);
}
}
В данном примере тестируется отправка письма по внутренней почте приложения, а так же проверка его отображения среди исходящих писем.
А теперь подробнее. Функции «seleniumStart» и «seleniumStop» начинают и завершают сессию. В первой функции инициализируется экземпляр класса Selenium, который посылает команды серверу. В него передаются такие параметры, как хост и порт (на котором запущен сервер Selenium), браузер (в котором будет проводиться тестирование) и url (по которому доступно тестируемое приложение).
Далее идет функция «printError», которая принимает в качестве параметров название теста и номер шага и выводит их в консоль. Вызывается эта функция при достижении таймаута на каком-либо шаге теста.
Чтобы не писать в каждом шаге блок повторяющегося кода, он был вынесен в отдельную функцию, называемую «testExec». Ее параметрами являются название и номер выполняемого шага (которые передаются в «printError» по истечению таймаута), команда для сервера, путь (локатор) к элементу в приложении, до которого пытаемся «достучаться», и таймаут. Обратите внимание, что путь приложения в приведенном примере имеет 2 типа: «VerifyProperty» и «UIEvent ». Первый используется для получения\сравнения значений атрибутов какого-либо элемента, а второй для осуществления действия над указанным элементом, например, кликнуть на кнопке. Указать можно UI-элемент приложения не только по его имени или id, но и по совокупности нескольких его свойств, которые дополнительно указываются в тегах «arg». Более подробную документацию можно получить на сайте Gorilla Logic.
Чтобы не писать локаторы вручную, я записывал последовательность шагов через FlexMonkey, экспортировал тесты в xml файл и вставлял в Java-код. В результате получался такой результат:
<FlexMonkey>
<TestSuite name="NewTestSuite" description="" ignore="false">
<TestCase name="Почта" description="null" ignore="false">
<Test name="Отправка почты" description="null" defaultThinkTime="250" ignore="false">
<UIEvent command="Select" value="tabBar" prop="automationName" delay="1000" attempts="10">
<arg value="Почта"/>
</UIEvent>
<Pause duration="10000"/>
<UIEvent command="Select" value="buttonBarMail" prop="automationName" delay="1000" attempts="10">
<arg value="Написать"/>
</UIEvent>
<Pause duration="1000"/>
<UIEvent command="Open" value="userDataProvider" prop="automationName" delay="1000" attempts="10">
<arg value="null"/>
</UIEvent>
<UIEvent command="Select" value="userDataProvider" prop="automationName" delay="1000" attempts="10">
<arg value="Василий Пупкин"/>
</UIEvent>
...
Или же можно установить плагин flexmonkium.xpi в дополнении к Selenium IDE и делать запись тестов непосредственно из браузера Firefox.
Но во всяком случае эти 2 способа не гарантируют 100% точной работы тестов, особенно когда компоненты приложения имеют динамически генерируемый ID. В этом случае придется вручную дописать дополнительные аргументы, явно присущие требуемому компоненту. Свойства компонентов можно посмотреть во FlexMonkey или FlexMonkium Console.
Результат
На данный момент на проекте автоматизировано большинство регрессионных тестов. Их количество составило > 350, время выполнения которых примерно 3-4 часа.
Прошу прощения за такой большой кусок кода в примере, который раздул объем статьи.