Готові рішення для QA: як писати автотесты на Groovy

Стаття підготовлена на основі доповіді Ярослава Святкіна на конференції QA Fest . Ярослав — тренер QA Automation & Groovy, Senior Test Automation Engineer, Consultant, GlobalLogic. Спеціалізації: автоматизація web і mobile (Android і iOS), пише на Java і Groovy.

У цій статті я поділюся, як швидко писати тести на мові програмування Groovy, не думати про фреймворку, PageObject і ініціалізації WebDriver. Фреймворк це складно? Ні! Я покажу спосіб, який дозволяє думати про тестування програми, а не про структуру коду. Я розповім про трьох фреймворках — Serenity, Selenide і Geb.

«Все, тепер починаємо автоматизацію!»

Перше, з чого варто почати автоматизацію, — подумати, що потрібно зробити, щоб нічого не писати, а якщо і писати, то як можна менше. Чим менше ви пишете, тим менше помилок ви робите. До того ж у вас з'являється вільний час, щоб випити кави, поспілкуватися з друзями або зайнятися самоосвітою.

Друге — не витрачати час на винахід велосипеда для створення тестового фреймворка. Що зазвичай робить більшість? Ми відкриваємо бібліотеку Selenium, пишемо рівні фреймворку — сторінки, тести, бізнес-логіку і т. д. Після цього всього ще обов'язково витрачаємо дорогоцінний час на створення звіту, який не соромно було б показати клієнту.

Велосипед, або Що ми зазвичай робимо все

Перше, що ми шукаємо, — як описувати сторінки, що могло б нам у цьому допомогти. В описі моделі PageObject доцільно використовувати елемент Html Elements.PageObject. Хтось його використовує, хтось-ні, але він дає змогу описати певні елементи і використовувати їх повторно.

@Name("Search form")
@FindBy(xpath = "//form")
public class SearchArrow extends HtmlElement {

 @Name("Search request input")
 @FindBy(id = "searchInput")
 private TextInput requestInput;

 @Name("Search button")
 @FindBy(className = "b-form-button__input")
 private Button searchButton;

 public void search(String request) {
requestInput.sendKeys(request);
searchButton.click();
}
}

Ще одне корисне засіб — додатковий фреймворк JDI UI Test Automation Framework. Він допомагає в основному створювати об'єкти: check boxes, text areas і т. д. з додатковим мета-тегами і всім необхідним. Цей фреймворк допомагає описати PageObject.

@JSite(domain = "https://jdi.com")
public class JDIExampleSite extends WebSite {
 public static HomePage homePage;

 public static LoginForm loginForm;

 @FindBy(css = ".profile-photo")
 public static Label profilePhoto;

 public static void login() {
profilePhoto.click();
 loginForm.loginAs(new User());
}
}

Для рівня опису сторінок він підходить, але завжди хочеться більшого — все максимально автоматизувати. На відміну від бібліотек, які допомагають працювати для опису моделі в рамках PageObject, існують тестові фреймворки з вже готовими рішеннями. Гнучкість, звичайно, втрачається, у цьому й відмінність готового рішення від самопісного. Власний «велосипед» завжди більш гнучкий.

Ось три готових рішення вам в допомогу.

Стандартне рішення — Serenity

Якщо ви хочете використовувати тестовий фреймворк, ви можете скористатися певним комплексом готових рішень. Один з них — Serenity. Це доцільно, якщо у вас є не дуже досвідчена команда, а вам потрібно побудувати процес автоматизації з нуля.

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

Project structure

Отже, у нас є рівень PageObject для опису сторінок, є кроки і тест — це класичний підхід до побудови тестового фреймворка. Ми окремо розподіляємо рівень сторінок (опис нашого додатка), окремо виносимо бізнес-логіку. При використанні бізнес-логіки пишемо тести, щоб вони не зверталися до сторінок безпосередньо.

PageObject ми використовуємо в наступному вигляді:

Я думаю, всі бачили FindBy і знають, як він пишеться (селектори і т. д.). А ось як виглядають кроки, опис бізнес-логіки:

Що важливо — гарно оформлений, готовий до роздруківці звіт:

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

Оптимальне рішення — Selenide

Тестовий фреймворк Selenide останнім часом стали активно використовувати. На нього іноді навіть погоджуються і замовники, хоча багато з них все ж вимагають виключно Java 7 і Selenium. Це відбувається з-за того, що створені автотесты потрібно підтримувати, а ті, хто буде їх підтримувати, про нових фреймворках можуть не знати.

Найбільший плюс — ми можемо поставити знак $ (улюблений знак з точки зору айтішників) і далі написати все, що хочемо. Реалізація вейтов вже існує:

Готове рішення — Geb

Geb — готове рішення, яке можна сміливо використовувати при написанні вашого фреймворка. На сайті gebish.org доступна дуже хороша документація, яка постійно оновлюється актуальними доповненнями. Geb дуже інформативний. Ви можете порівнювати все, не треба підключати сторонні бібліотеки, як це робиться в JUnit і Hamcrest.

Перша перевага Geb — використання мови Groovy. З одного боку, це плюс. По-перше, щоб писати на Groovy, треба писати дуже мало. По-друге, він використовує Java, тому всі Java-бібліотеки, які у нас є, ми можемо «переиспользовать». Додатково — в ньому чудові asserts, які не треба розписувати, як в TestNG або JUnit. Великий мінус в тому, що, по-перше, щоб команда починала писати на Groovy, вам треба спочатку навчити команду. По-друге, не завжди замовник готовий до використання Groovy, частіше до Java, а краще Java 7.

Аналогічно Selenide, в Geb можна використовувати знак $. Тут у нас є вже готові методи waitFor. Якщо раніше нам доводилося їх описувати, то в Geb ми просто передаємо заготовки, щоб повідомити, скільки треба чекати. Є і waitFor за замовчуванням, що дозволяє чекати до певного моменту. У наступному прикладі описана ситуація, коли необхідно почекати, поки не буде відображений певний елемент:

import geb.Browser

Browser.drive {
 go "http://gebish.org"

 assert title == "Geb - Very Groovy Browser Automation" 
 $("div.menu a.manuals").click() 
 waitFor { !$("#manuals-menu").hasClass("animating") } 
 $("#manuals-a menu")[0].click() 
assert title.startsWith("The Book Of Geb") 
}

Важко писати такий код? По-моєму, дуже легко. Природно, у нас є first-метод, за допомогою якого можна вибрати всі елементи зі знаком $. В даному випадку нам потрібен перший, тому ми вказуємо одиницю — «перший елемент» і «клік». Це класичний варіант.

При використанні Groovy у вас з'являється широкий спектр можливостей. Наведу кілька прикладів. Наприклад — створення random string для необхідної кількості значень можливо в один рядок:

Наступне — коли перед вами стоїть завдання перевірити API і у вас є HTTP-запит, ви отримуєте JSON-файл, який потрібно «розпарсити» і отримати певний результат. В Java вам потрібні сторонні бібліотеки для роботи з JSON-файлом. В Groovy ж підтримка парсинга і розбору JSON-файлу відбувається «коробочки».

У підсумку я став писати зовсім по-іншому. Я отримував JSON і привожу до ArrayList, і на виході у мене виходив ArrayList.

В Groovy будь масив може бути об'єктом. Ви берете певний масив і пишете: (massive as Duckling). Якщо все характеристики збігаються, у вас з'являється об'єкт Duckling. Крім того, у вас є можливість точно так само описувати JSON:

Вибір селекторів

Вибір селекторів величезний — і це найбільша проблема у їх виборі. Хтось любить CSS, а хтось XPath. Я ставлюся до останніх і міг би навести безліч аргументів на користь XPath.

Тут можна використовувати ID і клас By. Улюблений клас By дозволяє нам з того чи іншого селектору зробити вибір тих чи інших елементів.

Зверніть увагу на assert на зображенні. Тут є знак $. Вибираємо всі символи p в діапазоні з першого по другий. За допомогою методу *.text вибираємо весь текст цих елементів (значок * свідчить про те, що використовується метод мови Groovy), і нам повертається повний текст усіх елементів.

Скільки необхідно дій, щоб вибрати певний набір елементів: «всі комірки» або «всі рядки таблиці» — з таблиці з кожного елемента пробігтися і вибрати весь текст? Вам треба писати цикл, вибирати всі елементи, на кожному вибирати метод *.text і, відповідно, куди складати результати. Метод *.text повертає повністю список ваших текстів на всіх елементах.

В XPath іноді нам не вистачає можливості використовувати матчеры і, відповідно, регулярні вирази. Тут це можна використовувати прямо «з коробки». Це дуже великий бонус і дуже великий плюс для автоматизації процесу тестування. Природно, за текстом не дуже добре вибирати, але по класу ви можете це зробити.

Крім цього, є методи, з якими можна працювати як з рядками:

Також ще є і маса інших методів:

Один з варіантів, де показано, що нам доступно, в рамках Geb і Groovy в цілому, використовувати XPath з передачею аргументу:

Якщо порівнювати написання коду з допомогою Geb і класичний підхід, 25 рядків коду на Java замінить одна строчка на Groovy c Geb.

Що ще може вам допомогти в роботі над проектами? У нас є поняття методів wait, і не завжди на сторінці є ті чи інші елементи. Для перевірки ми пишемо метод isElementPresent, робимо track catches, обробляємо, повертаємо true або false.

І знову в Geb все це є в готовому вигляді. Перше, у нас є wait, який визначає, чекати елемент на сторінці чи ні. Ми можемо не чекати появи цього елемента, а перевірити його наявність.

Існує wait, який визначає, скільки секунд чекати при пошуку елементів. Ви відразу ставите його в селекторі при описі PageObject. Елемент може приймати параметр required true або false), тобто визначає, чи ні цей елемент знаходиться на цій сторінці.

Для кешування в Geb теж є варіант. Ви можете прямо в селекторах вказати:

Робота з таблицями

Крім того, що в Geb є готові елементи, також є можливість використання двох основних класів для побудови PageObject-моделі: сторінка і настроюється модуль.

Раніше у вас була таблиця, де потрібно було вибирати всі елементи, вибудовувати в певні об'єкти і з ними надалі працювати. Тут же цього робити не потрібно — кожний рядок є певним модулем зі своїм набором значень. В рамках цього модуля ви завжди можете повернути набір модулів з таблиці і з ним викликати необхідні вам методи.

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

Як це робиться? Давайте подивимося:

В описі PageObject в Geb є NotificationModule, а також елементи:

Посилання на елемент є посиланням на набір об'єктів іншого модуля. Це все пишеться в одну сходинку. Notification у нас є додатковим модулем з ім'ям і датою. Зауважте, на модулі відразу викликається метод, який викликає цей елемент.

Зрештою, я перестав писати методи на елементах. Якщо я звертаюся до певного елемента, мені відразу повертається або текст, або я клікаю по ньому, чи виконую інші дії прямо в рамках PageObject.

Тепер про результати. Завдяки searchResult будуть повністю повертатися всі знайдені тексти для вибраних елементів. Якщо ви будете використовувати link, у вас вийде наступне:

Якщо вам потрібно викликати всі методи — ставите «зірочку» і точку (*.), і вам повертається все, що ви хочете (це Groovy).

При класичному підході нам необхідно перевірити, на тій чи сторінці ми знаходимося. Тобто після переходу на сторінку перевірити її релевантність, далі діяти залежно від результату. Про це розробники Geb також подбали і запропонували готовий варіант:

Коли ви пишете PageObject, у вас використовується метод static at. Ви просто вказуєте, який елемент на сторінці повинен бути присутнім в поточному стані, якщо ви знаходитесь на конкретній сторінці.

Якщо вам необхідно перевірити, чи ви знаходитесь на сторінці, у вас є чудовий метод isAt. Ви передаєте поточну сторінку, і у вас виходить перевірка true/false (на поточній сторінці чи ні). Знову ж Geb це все йде «коробочки».

Ось класичний приклад такої сторінки (з логіном та паролем):

Якщо ви хочете справити метод логування, у вас класично передаються логін/пароль, повертаються елементи, ви очищаєте і з допомогою Groovy вводите значення. Метод login.submit знаходить і викликає кнопку, якою ви відразу ж клікаєте.

Для опису цих дій на Java 7 з використанням Selenium доведеться створювати набагато більший обсяг коду, ніж наведений вище. З точки зору програми, використання Geb дуже спрощує опис PageObject.

Взаємодія з мишею і підтримка кнопок

У Selenium у нас є набір Actions — дій, які ми можемо використовувати:

Для цього потрібно створити об'єкт Action, виконати певні дії для нього і зробити ці дії. У Geb ви можете виконати ті ж дії .clickAndHold, .moveByOffset і будь-які інші. Розробники фреймворку Geb передбачили все «під ключ». Залишається єдине питання: чому ми не використовуємо?

Тут є і підтримка кнопок:

Іноді ми відчуваємо великі труднощі — як запровадити ті чи інші значення або поля. Коли з'являються Windows-вікна, ми використовуємо хто на що здатний. У Geb є можливість використовувати будь-які кнопки, вкладки, переходи.

Раніше відкриті вікна і тести

Ще одна проблема — перехід між вікнами при відкритті посилань. Ми переходимо на наступне вікно, виконуємо в ньому певні дії, перевіряємо, що вони завершені, після чого повертаємося в попереднє вікно. У Google можна знайти величезну кількість рішень і скопіювати потрібний фрагмент коду. На цей складний алгоритм є готове рішення в Geb:

Для цього в Geb передбачені методи withWindow і withNewWindow. Чим вони відрізняються? Метод withWindow — нове вікно відкривається в існуючому, виробляються дії перевірки і відразу ж повертається в попереднє вікно.

withNewWindow відкриває нове вікно, що набагато спрощує процедуру. Для цього ви вказуєте, яку дію треба виконати, щоб перейти на інше вікно — клікаємо і перевіряємо, що ми знаходимося на поточній сторінці (у рамках нового вікна). Коли дія закінчується, автоматично ми повертаємося назад в попереднє вікно. Ніяких додаткових дій не потрібно.

Скільки рядків коду вам потрібно написати або скопіювати, щоб це реалізувати? У Geb весь тест буде записаний в один рядок, нічого складного:

Відкриваємо меню, відкриваємо браузер, переходите в нове вікно натискаємо assert, перевіряємо startsWith. Другий варіант тесту аналогічний першому, але тільки передбачає відкриття нового вікна.

Показ прихованого елементу

Досить часто ми використовуємо в коді JS, так як є речі, які без нього зробити не можна. Наприклад, реалізувати scroll. Для цього потрібно написати певний шматок коду, привести його до Java executor та інше.

І знову в Geb є все, що нам необхідно. Якщо ви хочете використовувати JS-код, вам треба всього лише написати js.exec і вказати, що саме ви хочете виконати:

Крім того, що в Geb є вбудована підтримка JS, підтримка jQuery. Це може значно розширити можливості з точки зору фреймворку і оптимального опису необхідних дій.

Підтримувані браузери

У Geb підтримуються всі браузери, які є в рамках WebDriver. Зазвичай в наших рішеннях ми робимо driver factory, окремо описуємо необхідні браузери, далі його «підтягуємо» і запускаємо — з урахуванням специфіки job. У фреймворку Geb це винесено за замовчуванням. Тут вже є окремий файл — GebConfig.groovy, який ми повинні створити за замовчуванням і в ньому описати браузери, а також тестову середу:

Сюди можна повністю винести всі необхідні для тестів константи (ім'я, пароль, e-mail), налаштування тестів, правила перевірки, тайм-аут за замовчуванням та інше. Далі — область тестового середовища (test environment), для якої ми задаємо імена оточення (Chrome, Firefox, IE) і створюємо ті драйвера, які необхідні.

Зрозуміло, що в рамках використовуваного build (Gradle або Maven) створюються певні завдання. При цьому для Gradle build необхідно прописати властивості системи (System Properties), а саме з яким environment будуть запускатися тести, де лежать драйвера тієї чи іншої тестового середовища:

В даному випадку ми використовуємо фреймворк TestNG і бачимо приклад запуску тестового набору (test suite). В залежності від того, які завдання ви створили, у вас буде запускатися та чи інша тестова середовище в рамках існуючого.

Якщо ви налаштовуєте Jenkins, ви запускаєте job та тестові набори стартують у певної середовищі. Все це працює легко і зрозуміло. Навіть новачок у команді може дуже швидко і легко розібратися, як стартує ваш проект, на який середовищі, де знаходяться налаштування і як це все запускати.

Інтеграції з mobile

Geb і Spock — це одна зв'язка, нові підходи у BDD. Особисто у мене не дуже вийшло використовувати Geb+Spock, і ось чому. Коли я пропонував команді використовувати Geb — всі погоджувалися, але коли я запропонував використовувати ще і Spock, це порахували занадто складним рішенням, так як необхідно переходити повністю на новий стек технологій.

Але дуже добре у мене вийшло використовувати Geb з Cucumber. У нього дуже хороша підтримка, BDD, добре оформлений звіт. Всередині, звичайно ж, була підтримка Geb і всі сторінки описувалися за допомогою Geb. Підтримуються також JUnit і TestNG, і всі тестові перевірки на базі цих фреймворків можна робити в Geb.

Бібліотека Geb Mobile допомагає створювати і використовувати Geb для нативного Android або iOS-додатку. Звичайно, дещо не працюватиме у зв'язку зі специфікою нативних мобільних додатків, але побудова PageObject, модулів можна застосовувати і до цих додатків.

Переваги і недоліки готового рішення

На закінчення перелічимо переваги і недоліки використання Geb.

Переваги:

Недоліки:

У кожного рішення є свої переваги і свої недоліки. При виборі технологічного стека визначатися вам! Цей вибір залежить від складу і знань вашої команди і завдань, які ви хочете вирішити.

Опубліковано: 13/04/18 @ 10:00
Розділ Різне

Рекомендуємо:

DOU Labs: як у GlobalLogic витворили SmartHome для керування пристроями від різних виробників
Technical writer: як потрапити в професію і що вчити
DOU Проектор: CLAP — розумний будинок українського виробництва
Java дайджест #38: Java 10
Ruby/Rails дайджест #16: офіційний реліз Rails 5.0.7 і 5.1.6, нова бета-версія Hanami, створюємо Slack bot на Rails