Привет!
Этот небольшой очерк адресован QA – специалистам и в большей степени разработчикам, которые привлечены к автоматизации тестирования вэб и мобильных приложений. Те, кто просто интересуется open source' ом — тоже welcome.
Здесь я хочу развить мысли, высказанные год назад в статье «Про Selenium и один «велосипед»».
План:
1. Основные фичи (краткий обзор)
2. Как развивалось (лирическое отступление)
3. Заключение.
Вы можете сразу ознакомиться с решением. Но если интересно сначала прочитать статью —
поехали!


Как вы уже поняли, решение написано на Java 8. Основными компонентами являются Selenium (для обеспечения взаимодействия с десктопными браузерами), Appium (java-client, для обеспечения взаимодействия с мобильными браузерами и приложениями).
Если говорить о принципе и способе работы с Selenium Webdriver, то он в чем-то похож на то, что предлагают такие решения как Html elements от Яндекс и Thucydides. Но все это несколько пересмотрено.
То, что получилось, у меня вызывает ассоциации с каком-то членистоногим существом. Учитывая, что Selenium и Appium находят свое применение в автоматизации тестирования + мы все знаем перевод слова bug, то получилось такое название для решения — Arachnidium (лат. «паукообразный»).
Итак.
Я думаю этот пункт подразумевается как нечто само собой разумеющееся. Но упомянуть надо.
Поддерживаются:
— Firefox;
— Chrome;
— Internet Explorer;
— Safari;
— PhantomJS.
— Удаленный запуск перечисленных выше браузеров.
От поддержки HtmlUnitDriver и OperaDriver пришлось отказаться. Первый не является наследником RemoteWebDriver и его поддержка чревата костылями, второй устарел (например, он не запускает Opera на Windows 8/8.1). Но есть актуальная замена:
— Chrome для Android. Чуть позже хочу добавить поддержку родного браузера Android и Chromium
— Mobile Safari для iOS

Это возможно за счет интенсивного использования возможностей Appium.
Но, даже не это, на мой взгляд, самое интересное.
Подробно я описал эти принципы тут, тут и тут (по английски). Но чтобы не отвлекать читателей, я пожалуй кое-что процитирую, что-нибудь по-эффектнее.
Для своих тестов на Android я использую приложение BBC News. Его UI очень похож на UI сайта по визуальному составу элементов. Предположим, что нужно протестировать как сайт, так и приложение для Android. Тогда можно описать пользовательский интерфейс так.
Список и просмотр новостей:
Формочка выбора новостей по категориями:
Тест (самый упрощенный вид):
Android
Браузер (десктопный/мобильный)
О некоторых вещах я расскажу чуть позже.
Я постарался реализовать универсальную (скорее — условно универсальную) модель, чтобы разработчик фрэймворка для автотестов (а я вижу себя в этой роли, для себя я плохо не сделаю :)) не множил код а смог сделать его независимым от окружения (я понимаю под этим то, как тест выполняется — в браузере или это нативный контент/html контент гибридного приложения).
Используется дизайн — паттерн Page Object. Мой вариант предполагает, что страницы/скрины могут быть описаны как целиком, так и по частям, если есть повторяющиеся виджеты или наборы элементов. Можно даже заставить целое приложение вести себя как Page Object!
Еще одной особенностью является то, что многие технические нюансы, связанные с необходимостью управления экземпляром WebDriver'а в тех ситуациях, когда одновременно присутствует несколько окон браузера (или контекстов, если мобильное приложение) и часть контента размещена в ifram'ах — автоматизированы. Так что можно полностью сосредоточиться на описании бизнес-логики!

В современных условиях, мое имхо, решает не монолитная архитектура, а модульная или «прозрачная». Я постарался реализовать все так, чтобы можно было использовать как стандартные решения Selenium и Appium (этот способ декорирования элементов в моем решении используется по умолчанию), для которых я постарался предусмотреть удобные способы работы, так и, теоретически — решения сторонних разработчиков.
Здесь примеры:
— совместной работы с HtmlElements от Яндекса. Ссылка.
— совместной работы с Selenide от Сodeborne. Ссылка.
— использования Thucydides. Ссылка. Кому интересно — отчет для web (GoogleDrive) и отчет для Android (BBC News, виртуальная машина Genymotion, эмулирующая Android-планшет). Приятного просмотра и не забудьте распаковать. Хочу позже сделать похожий сэмпл для Allure.
В данном случае я подразумеваю передачу и хранение параметров для запуска браузеров и мобильных приложений (так задумывалось с самого начала). Подробно описано здесь. Но как уж и быть. Приведу пример.
Пусть есть общая настройка, хранящаяся в файле settings.json, приложенном к проекту.
И есть файл, названный по другому, содержащий такие данные, которые как бы перекрывают данные из примера выше.
Код ниже
дает такой вывод на консоль
Таким образом, предполагается наличие общей настройки, в которой указаны дефолтные данные, и набора таких конфигураций, которые частично перекрывают или уточняют эти данные. При этом если данные не указаны в таком кастоме — будет использована общая информация.
Я предполагаю использовать такой механизм для старта браузеров и мобильных приложений. Но, в принципе, он может найти и более широкое применение благодаря своей гибкости и расширяемости.
На этом я заканчиваю свой обзор. Здесь есть и другие интересные вещи. Может быть, о них я расскажу в другой статье или в комментариях к этой.

Большую часть своей профессиональной биографии я занимался автоматизацией тестирования десктопного софта, главным образом с помощью Test Complete. Тут было много всего интересного и нетривиального. Но я устал. Потянуло на какое-то творчество.
Позже я узнал про Selenium Webdriver (а кто сейчас не знает?). Сначала были просто эксперименты. Потом стали появляться идеи. Хотя… Я их брал из накопленной практики. Например, такое понятие как Page Object и примеры реализации не вызвали у меня Wow – эффекта. Нечто подобное приходилось делать для десктопных приложений.
Описанный в главе выше эксперимент был прекращен и возобновлен спустя несколько месяцев.
Далее я узнал про Appium. Мне даже довелось поучаствовать в этом проекте! Участие началось спонтанно — с репортинга багов и того, что мне казалось проблемным. Позже появилась клиентская библиотека для java: java-client. Тут вклад серьезнее. Мне удалось реализовать фичи для работы с PageFactory и помочь с редизайном библиотеки, в результате которого появились AndroidDriver и IOSDriver и появилась гибкость, если понадобится добавить поддержку Firefox OS и Windows Mobile. Надеюсь, это помогло многим другим людям по всему миру.
Буду рад общению в комментариях.
Этот эксперимент я довел до такого состояния, что не стыдно сказать, что билды доступны в maven central. Пока я предложил бы с ними поиграть или попробовать автоматизировать какой-нибудь несложный тест-кейс.
Если кто-то найдет баг — это круто! Прошу описать его здесь. А в комментариях было бы здорово почитать критику, идеи, если кто-то похвалит — это тоже хорошо.
На описанном останавливаться не хочется. Есть идея продолжить банкет — реализовать плагины для JUnit, TestNG и Jbehave. Можно сделать плагин, например, для Eclipse IDE, но я с трудом представляю пока его функции. Кроме того — существует пока еще пустой C# проект. Но! Все это имеет смысл, если базовая функциональность нужна. Всегда рад пулл-реквестам!
До встречи!

Этот небольшой очерк адресован QA – специалистам и в большей степени разработчикам, которые привлечены к автоматизации тестирования вэб и мобильных приложений. Те, кто просто интересуется open source' ом — тоже welcome.
Здесь я хочу развить мысли, высказанные год назад в статье «Про Selenium и один «велосипед»».
План:
1. Основные фичи (краткий обзор)
2. Как развивалось (лирическое отступление)
3. Заключение.
Вы можете сразу ознакомиться с решением. Но если интересно сначала прочитать статью —
поехали!

1. Основные фичи (краткий обзор)

Как вы уже поняли, решение написано на Java 8. Основными компонентами являются Selenium (для обеспечения взаимодействия с десктопными браузерами), Appium (java-client, для обеспечения взаимодействия с мобильными браузерами и приложениями).
Если говорить о принципе и способе работы с Selenium Webdriver, то он в чем-то похож на то, что предлагают такие решения как Html elements от Яндекс и Thucydides. Но все это несколько пересмотрено.
То, что получилось, у меня вызывает ассоциации с каком-то членистоногим существом. Учитывая, что Selenium и Appium находят свое применение в автоматизации тестирования + мы все знаем перевод слова bug, то получилось такое название для решения — Arachnidium (лат. «паукообразный»).
Итак.
Кроссбраузерность
Я думаю этот пункт подразумевается как нечто само собой разумеющееся. Но упомянуть надо.
Поддерживаются:
— Firefox;
— Chrome;
— Internet Explorer;
— Safari;
— PhantomJS.
— Удаленный запуск перечисленных выше браузеров.
От поддержки HtmlUnitDriver и OperaDriver пришлось отказаться. Первый не является наследником RemoteWebDriver и его поддержка чревата костылями, второй устарел (например, он не запускает Opera на Windows 8/8.1). Но есть актуальная замена:
— Chrome для Android. Чуть позже хочу добавить поддержку родного браузера Android и Chromium
— Mobile Safari для iOS
Поддержка автоматизации взаимодействия с UI нативных и гибридных мобильных приложений.

Это возможно за счет интенсивного использования возможностей Appium.
Но, даже не это, на мой взгляд, самое интересное.
Возможность моделировать пользовательский интерфейс приложения как по частям так и в целом.
Подробно я описал эти принципы тут, тут и тут (по английски). Но чтобы не отвлекать читателей, я пожалуй кое-что процитирую, что-нибудь по-эффектнее.
Для своих тестов на Android я использую приложение BBC News. Его UI очень похож на UI сайта по визуальному составу элементов. Предположим, что нужно протестировать как сайт, так и приложение для Android. Тогда можно описать пользовательский интерфейс так.
Список и просмотр новостей:
Код под катом
/** * Imagine that we have to check browser and Android versions * How?! See below. */ @IfBrowserURL(regExp = "http://www.bbc.com/news/") @IfMobileContext(regExp = "NATIVE_APP") @IfMobileAndroidActivity(regExp = "HomeWwActivity") public class BBCMain extends FunctionalPart<Handle>{ @FindBy(className = "someClass1") @AndroidFindBy(id = "bbc.mobile.news.ww:id/articleWrapper") private List<RemoteWebElement> articles; @FindBy(className = "someClass2") @AndroidFindBy(id = "bbc.mobile.news.ww:id/articleWebView") private RemoteWebElement currentArticle; @FindBy(className = "someClass3") @AndroidFindBy(id = "bbc.mobile.news.ww:id/optMenuShareAction") private RemoteWebElement share; @FindBy(className = "someClass4") @AndroidFindBy(id = "bbc.mobile.news.ww:id/optMenuWatchListenAction") private RemoteWebElement play; @FindBy(className = "someClass5") @AndroidFindBy(id = "bbc.mobile.news.ww:id/optMenuEditAction") private RemoteWebElement edit; @FindBy(className = "someClass6") @AndroidFindBy(uiAutomator = "new UiSelector().resourceId" + "(\"bbc.mobile.news.ww:id/optMenuRefreshAction\")") private RemoteWebElement refresh; protected BBCMain(Handle context) { super(context); load(); } @InteractiveMethod public int getArticleCount(){ return articles.size(); } //some more staff //... }
Формочка выбора новостей по категориями:
Код под катом
/** * Imagine that we have to check browser and Android versions * How?! See below. */ @IfBrowserURL(regExp = "http://www.bbc.com/news/") @IfMobileContext(regExp = "NATIVE_APP") @IfMobileAndroidActivity(regExp = "PersonalisationActivity") public class TopicList extends FunctionalPart<Handle> { @CacheLookup @FindBys({@FindBy(linkText = "someLink"), @FindBy(linkText = "someLink2"), @FindBy(linkText = "someLink2")}) @AndroidFindBys({@AndroidFindBy(id = "bbc.mobile.news.ww:id/personalisationListView"), @AndroidFindBy(className = "android.widget.LinearLayout"), @AndroidFindBy(uiAutomator = "new UiSelector()"+ ".resourceId(\"bbc.mobile.news.ww:id/feedTitle\")")}) private List<WebElement> titles; @CacheLookup @FindBys({@FindBy(linkText = "someLink3"), @FindBy(linkText = "someLink4"), @FindBy(linkText = "someLink5")}) @AndroidFindBys({@AndroidFindBy(id = "bbc.mobile.news.ww:id/personalisationListView"), @AndroidFindBy(className = "android.widget.LinearLayout"), @AndroidFindBy(uiAutomator = "new UiSelector()."+ "className(\"android.widget.CheckBox\")")}) private List<WebElement> checkBoxes; @AndroidFindBy(id = "bbc.mobile.news.ww:id/personlisationOkButton") private WebElement okButton; protected TopicList(Handle context) { super(context); load(); } //some more staff //... }
Тест (самый упрощенный вид):
Android
Код под катом
@Test public void androidNativeAppTest() { Configuration config = Configuration .get("android_bbc.json"); Application<?,?> bbc = MobileFactory.getApplication( Application.class, config); try { BBCMain bbcMain = bbc.getPart(BBCMain.class); Assert.assertNotSame(0, bbcMain.getArticleCount()); bbcMain.selectArticle(1); Assert.assertEquals(true, bbcMain.isArticleHere()); bbcMain.edit(); TopicList<?> topicList = bbcMain.getPart(TopicList.class); topicList.setTopicChecked("LATIN AMERICA", true); topicList.setTopicChecked("UK", true); topicList.ok(); bbcMain.edit(); topicList.setTopicChecked("LATIN AMERICA", false); topicList.setTopicChecked("UK", false); topicList.ok(); } finally { bbc.quit(); } }
Браузер (десктопный/мобильный)
Код под катом
@Test public void webTest() { Configuration config = Configuration .get("android_some_browser.json"); Application<?,?> bbc = WebFactory.getApplication( Application.class, config, urlToBBCNews); //does the same
О некоторых вещах я расскажу чуть позже.
Я постарался реализовать универсальную (скорее — условно универсальную) модель, чтобы разработчик фрэймворка для автотестов (а я вижу себя в этой роли, для себя я плохо не сделаю :)) не множил код а смог сделать его независимым от окружения (я понимаю под этим то, как тест выполняется — в браузере или это нативный контент/html контент гибридного приложения).
Используется дизайн — паттерн Page Object. Мой вариант предполагает, что страницы/скрины могут быть описаны как целиком, так и по частям, если есть повторяющиеся виджеты или наборы элементов. Можно даже заставить целое приложение вести себя как Page Object!
Еще одной особенностью является то, что многие технические нюансы, связанные с необходимостью управления экземпляром WebDriver'а в тех ситуациях, когда одновременно присутствует несколько окон браузера (или контекстов, если мобильное приложение) и часть контента размещена в ifram'ах — автоматизированы. Так что можно полностью сосредоточиться на описании бизнес-логики!
Архитектура.
В современных условиях, мое имхо, решает не монолитная архитектура, а модульная или «прозрачная». Я постарался реализовать все так, чтобы можно было использовать как стандартные решения Selenium и Appium (этот способ декорирования элементов в моем решении используется по умолчанию), для которых я постарался предусмотреть удобные способы работы, так и, теоретически — решения сторонних разработчиков.
Здесь примеры:
— совместной работы с HtmlElements от Яндекса. Ссылка.
— совместной работы с Selenide от Сodeborne. Ссылка.
— использования Thucydides. Ссылка. Кому интересно — отчет для web (GoogleDrive) и отчет для Android (BBC News, виртуальная машина Genymotion, эмулирующая Android-планшет). Приятного просмотра и не забудьте распаковать. Хочу позже сделать похожий сэмпл для Allure.
Способ настройки
В данном случае я подразумеваю передачу и хранение параметров для запуска браузеров и мобильных приложений (так задумывалось с самого начала). Подробно описано здесь. Но как уж и быть. Приведу пример.
Пусть есть общая настройка, хранящаяся в файле settings.json, приложенном к проекту.
JSON с дефолтными параметрами
{ "settingA": { "aValue":{ "type":"STRING", "value":"AAA" } }, "settingB": { "bValue":{ "type":"STRING", "value":"bbb" } }, "settingC": { "cValue":{ "type":"STRING", "value":"C" } }, "settingD": { "dValue":{ "type":"STRING", "value":"D..." } } }
И есть файл, названный по другому, содержащий такие данные, которые как бы перекрывают данные из примера выше.
JSON с кастомными параметрами
{ "settingB": { "bValue":{ "type":"INT", "value":"1" } }, "settingC": { "cValue":{ "type":"BOOL", "value":"true" } } }
Код ниже
код
import com.github.arachnidium.util.configuration.Configuration; import org.junit.Before; import org.junit.Test; public class DemoTest { Configuration testConfig; private String aGroup = "settingA"; private String bGroup = "settingB"; private String cGroup = "settingC"; private String dGroup = "settingD"; private String aValue = "aValue"; private String bValue = "bValue"; private String cValue = "cValue"; private String dValue = "dValue"; @Before public void setUp() throws Exception { testConfig = Configuration.get("src/test/resources/test.json"); } @Test public void test() { Object a = Configuration.byDefault.getSettingValue(aGroup, aValue); Object b = Configuration.byDefault.getSettingValue(bGroup, bValue); Object c = Configuration.byDefault.getSettingValue(cGroup, cValue); Object d = Configuration.byDefault.getSettingValue(dGroup, dValue); System.out.println(a); System.out.println(a.getClass()); System.out.println(b); System.out.println(b.getClass()); System.out.println(c); System.out.println(c.getClass()); System.out.println(d); System.out.println(d.getClass()); System.out.println(); System.out.println(); System.out.println("Showtime! Customized setting see below."); System.out.println(); System.out.println(); a = testConfig.getSettingValue(aGroup, aValue); b = testConfig.getSettingValue(bGroup, bValue); c = testConfig.getSettingValue(cGroup, cValue); d = testConfig.getSettingValue(dGroup, dValue); System.out.println(a); System.out.println(a.getClass()); System.out.println(b); System.out.println(b.getClass()); System.out.println(c); System.out.println(c.getClass()); System.out.println(d); System.out.println(d.getClass()); } }
дает такой вывод на консоль
то, что вывела консоль
AAA class java.lang.String bbb class java.lang.String C class java.lang.String D... class java.lang.String Showtime! Customized setting see below. AAA class java.lang.String 1 class java.lang.Integer true class java.lang.Boolean D... class java.lang.String
Таким образом, предполагается наличие общей настройки, в которой указаны дефолтные данные, и набора таких конфигураций, которые частично перекрывают или уточняют эти данные. При этом если данные не указаны в таком кастоме — будет использована общая информация.
Я предполагаю использовать такой механизм для старта браузеров и мобильных приложений. Но, в принципе, он может найти и более широкое применение благодаря своей гибкости и расширяемости.
На этом я заканчиваю свой обзор. Здесь есть и другие интересные вещи. Может быть, о них я расскажу в другой статье или в комментариях к этой.
2. Как развивалось (лирическое отступление).

Большую часть своей профессиональной биографии я занимался автоматизацией тестирования десктопного софта, главным образом с помощью Test Complete. Тут было много всего интересного и нетривиального. Но я устал. Потянуло на какое-то творчество.
Позже я узнал про Selenium Webdriver (а кто сейчас не знает?). Сначала были просто эксперименты. Потом стали появляться идеи. Хотя… Я их брал из накопленной практики. Например, такое понятие как Page Object и примеры реализации не вызвали у меня Wow – эффекта. Нечто подобное приходилось делать для десктопных приложений.
Описанный в главе выше эксперимент был прекращен и возобновлен спустя несколько месяцев.
Далее я узнал про Appium. Мне даже довелось поучаствовать в этом проекте! Участие началось спонтанно — с репортинга багов и того, что мне казалось проблемным. Позже появилась клиентская библиотека для java: java-client. Тут вклад серьезнее. Мне удалось реализовать фичи для работы с PageFactory и помочь с редизайном библиотеки, в результате которого появились AndroidDriver и IOSDriver и появилась гибкость, если понадобится добавить поддержку Firefox OS и Windows Mobile. Надеюсь, это помогло многим другим людям по всему миру.
3. Заключение.
Буду рад общению в комментариях.
Этот эксперимент я довел до такого состояния, что не стыдно сказать, что билды доступны в maven central. Пока я предложил бы с ними поиграть или попробовать автоматизировать какой-нибудь несложный тест-кейс.
Если кто-то найдет баг — это круто! Прошу описать его здесь. А в комментариях было бы здорово почитать критику, идеи, если кто-то похвалит — это тоже хорошо.
На описанном останавливаться не хочется. Есть идея продолжить банкет — реализовать плагины для JUnit, TestNG и Jbehave. Можно сделать плагин, например, для Eclipse IDE, но я с трудом представляю пока его функции. Кроме того — существует пока еще пустой C# проект. Но! Все это имеет смысл, если базовая функциональность нужна. Всегда рад пулл-реквестам!
До встречи!
