Как я писал на Groovy и что за зверь такой GEB… (часть 3)

Tymur Kubai
5 min readJul 2, 2017

--

Ранее — Часть 1 и Часть 2

SPOCK

Кратенько.

В spock все тестовые классы должны наследоваться от класса spock.lang.Specification.

class MySpec extends Specification {
def “pass me please” () {
expect:
//to be true
}
}

Ну иначе фигушки тебе, а не запуск тестов, т.к. ты никак не помечаешь что данный метод является тестом ты помечаешь класс и все методы в нем являються тестами(кроме пре/пост-кондишеннов setup(), cleanup(), setupSpec(), cleanupSpec()).

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

  • @Ignore(“Shabbat”) или просто @Ignore причем можно как тесты ей игнорить так и целые классы(они же спеки)
  • @IgnoreIf({env.isRepublican()}) то же не рокетсаенс, будет игнорить при совпадении условий
  • @IgnoreRest игнорит всё кроме методов обладающих данной аннтоацией
  • @Requires({env.isRepublican()}) противоположность @IgnoreIf тест будет запушен только при совпадении условия иначе скипнет
  • @PendingFeature если тесты падают, то помечаються как пропущенные

За доп инфой милости просим в доки spock.

GEB — тыць

Geb is a browser automation solution.

Именно решение для автоматизации, не обязательно тестовой. Хотя если использовать с jUnit/TestNG/Spock то будут вам и тесты. Написан GEB на groovy и являющийся оберткой над WebDriver.

Собственно где groovy там и скрипты, а суть скрипта в последовательном выполнении пачки операций.

Так как консистентность залог успеха, то выбирать под groovy сразу нужно spock(поэтому и делалась отсылка в самом начале).

GebSpec

По аналогии с spock где все классы наследовались от Specification, то в GEB наследуються от GebSpec.

Собственно ничем друг от друга они не отличаются.

class MySpec extends GebSpec {    def “pass me please” () {    expect:
//to be true

}
}

Page object

Начнем с того что в java при использовани page object хорошей практикой для уменьшения дублирования кода(локаторов) выступаеть композиция. А если в страницах используется наследование которое подразумевает в себе вынесение в базовый класс локаторов, то как показывает практика выходит полная атмта.

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

Пример страницы:

import geb.Pageclass SearchPage extends Page{static url = "search"static at = { title == 'Find me beatch!' }static content = {
header { module(HeaderModule)}
inputSearch { $('#login') }
btnSearch { $('.submit') }
}
def findByClick(text) {
inputSearch.value(text);
btnSearch.click();
}
}

Пример модуля:

import geb.Module

class HeaderModule extends Module{

static content = {
logo { $('img.logo') }
}

void goHome(text) {
logo.click()
}
}

А теперь немного объяснений.

Если в конфиг файле или пачкой других способов описанных в доках задать базовый урл, то у страницы можно указывать относительный урл в виде static url = "search" при навигации он будет складывать его с базовым.

Локары и модули объявляються в виде замыкания(aka closure, всегда пишу и говорю замыкание так как не хочу путать с одноименным языком програмирования). Объявляется как static content {//smth} и дальше уже в кастомных методах данной страницы/модуля мы можем обращаться к объявленным в этом замыкании полям.

Про static at = { title == 'Find me beatch!' }хочется расказать отдельно.

Навигация по страницам.

Как-то расказывая своему другу-программисту о page object. И как наиный ребенок устами которого глаголит истина он спрашивал меня про то как определяется где и как упал тест?

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

Кстати потом видел такие же мысли и на конфах.

К чему я все это, а в GEB возможность дефолтной проверки такого состояния включено в page object и это сделанно за счет замыкания static at = {//some assert}

И при навигации с помощью метода to(PageName) будет выполнена навигация на static url и валидация замыкания static at.

Если валидация на static at не нужна, а нужна только навигация, то это делается с помощью метода via(PageName).

Можно отдельно проверить что мы на нужной странице вызвав at(PageName).

Особняком стоит page(PageName) который не делает навигации, а просто устанавливает текущий инстанс страницы.

При при использовании любого из page(PageName) to(PageName) и via(PageName). Скрипт устанавливает текущее значение страницы и все дальнейшие действия будут производиться над ней.

Пример на основе приведеных выше SearchPage и HeaderModule:

class MySpec extends GebSpec {    def “example” () {        expect:
to(SearchPage)
findByClick('pussy slave')
header.goHome()
at(HomePage)
page(HomePage)
}
}

Что не понравилось в GEB:

На мой вкус, слишком перегрузили обертку над WebElement. В GEB она называется Navigator. Просто полотно методов при вызове автодополнении в IDE. Плюсь очень не очевидно что получением и установлением значения в input сделанно перегрузкой метода value()

$(‘#id’).value()         //return value
$(‘#id’).value(sometext) //set value, return void

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

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

Исходя из всего вышеперечисленного, то входной гейт во фреймворк очень большой. Даже не gate, а GATE.

Благодарность

Спасибо что дочитали до конца) Надеюсь статься была полезна.

P.S.

Извиняюсь за скомканный гайд по GEB, но лучше так чем вобще не закончить.

“Лучше сделать хорошо, но никогда, чем кое-как и сегодня”.

А так как завтра мой первый рабочий день на новом месте и groovy и geb-ом там не пахнет, то решил поставить точку над i.

Всем благ :)

--

--

Responses (1)