Как я писал на Groovy и что за зверь такой GEB… (часть 1)
Очередное тестовое.
Вышли на меня через LinkedIn выслали тестовое, про его эстимацию я уже писал(и всеравно я занизил сроки, рукалицо). Вакансия от конторы agileengine, на случай если ссылка помрет приведу ниже описание.
Необходимые навыки
● 2+ year of experience as a Software Quality Assurance Engineer;
● At least 1 year experience with Java or Groovy/Grails and some Automation framework like Selenium Web Driver / Geb / Geb and Spock, Page Object Model;
● Experience in analyzing business requirements, recommending test strategies, and writing test plans and/or requirements;
● The ability to analyze business needs and conceive, design, and develop innovative and automated test solutions;
● Excellent verbal and written communication skills
Будет плюсом
● Knowledge of BDD test practices
Анализ тестового
Нужно было покрыть тестами несколько api запросов у twitter и после этого покрыть аналогичную функциональность через ui.
Сразу даю ссылку на готовое тестовое задание на github. И последняя стока из ТЗ:
Tests should be written using Java + Selenium WebDriver (ideally to be done with Geb)
Судя по вакансии чувакам нужен не Java Automation, а Groovy. Темболее Geb как раз обертка над селениумом написанная на Groovy. А джавистов они расматривают только как альтернартиву ибо грувистов мало. Выгодней всего писать все тесты(api + ui) на groovy, а для ui использовать тот самый Geb.
Почему выгодно?
Всегда выбирайте самый трудный путь — там вы не встретите конкурентов. Шарль де Голль.
Заодно groovy и spock подучу.
API тесты
Изначально мысль была покрывать апи твиттера использую связку rest-assured и десериализация json через jackson.
Но погуглив я гашел просто отличную либу для работы с twitter которая делает все хорошие вещи за меня. Особенно избавляет от гемора OAuth.
В итоге тесты получились очень короткими и удобочитаемыми.
Отдельно няшная штука это SPOCK.
Особенно доставило, то как реализован отлов ожидаемых exception в тесте.
Если в jUnit/TestNG это делается через параметр анотации.
@Test(expectedExceptions = WebDriverException.class)
Что позволяет только нормально проверить класс исключения. А если хочешь узнать текст или еще какие штуки которые в исключении прилетели, то нужно делать try-catch, в блоке try реализовывать asserts, a после еще и throw это же исключения что бы на уровне метода его словило и завилидировало как правильно поведение.
В spock же с этим красота:
then:
def e = thrown(TwitterException)
e.getStatusCode() == errCode
e.getErrorMessage() == errMs
в блоке then(а он у нас отвечает за валидацию) ты ловишь исключение и делаешь что с ним хочешь.
Зависимые тесты:
В тестовом задание прослеживается цепочка действий.
Добавить твит -> Попытаться продублировать твит -> Удалить твит.
Поэтому пришлось сделать тесты взаимозависимыми.
Особняком еще стоит тест - Проверить ленту твитов(пришлось на тестовом аккаунте отписаться от всех, иначе динамичные данные которые не контролирует автоматизатор просто будут фейлить данный тест через рандомный прмоежуток времени). Но я его решил оставить в том же классе где добавление/удалиние происходит и поставить на первую позицию.
Вот тут немного хитрее ситуация. Если в TestNG через аннотацию можно зависить от методов/групп(перечисленных через запятую):
@Test(dependsOnMethods = “test1, test2”)
@Test(dependsOnGroups = “group1, group2”)
В spock то есть похожая возможность, можно пометить что тесты зависят друг от друга. Но в данному случае ты ставишь аннотацию @Stepwise над всем классом и тесты зависят друг по их расположения в коде. Что впринципе ведет к более независимому тестовому дизайну. Тесты которые идут в одну цепочу должны лежать в одном отдельном классе.
Хотя в какой-то мере это может быть перегиб, так как с другой стороны в одном классе могут лежать несколько тестов покрывающие одну фичу/функционал но при этом не все из них могут быть взаимозависимы.
Под “независимым тестовым дизайном” я иммел ввиду что Spock располагает к тому что бы под конкретную фичу выделялся пакет и внутри него уже класс/классы ее покрывающие. Что обычно стоит делать и jUnit/TestNG
Короче палка о двух концах с аннотацией @Stepwise. Пример можно посмотреть либо в моём репозитории указано выше либо в офф примерах спока. Но хорошо что такая возможность есть :)
Параметризация(DDT — Data Driven Testing)
Начну пожалуй с TestNG. В основном тесты которые раняться несколько раз будут раниться с помощью @DataProvider. Этой аннотацией помечаються методы которые будут обеспечивать тест данными. В параметре аннотации @Test указывается какой DataProvider будет использован.
@DataProvider
public static Object[][] multiLetter() {
return new Object[][]{
{5, "Z"},
{15, "Z"},
};
}@Test(dataProvider = "multiLetter")
public void test1(int count, String letter){
Для spock же аналогие датапровайдеру служит блок where:
where — ответсвенен за данные поставляемые в тест. И может быть реализован в виде таблицы:
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
Либо в виде data pipes:
a << [1, 7, 0]
b << [3, 4, 0]
c << [3, 7, 0]
Таблицы менее гибки, так как они статичны, а вот с data pipes можно добиться той же динамики которую можно получить от DataProvider.
Я какраз так использовал data pipes для написания api тестов для твиттера.
where:
st << twitter.getHomeTimeline()
epochExp << epoch
retweetExp << retweetCount
textExp << textOfTweets
Список твитов в ленте я получаю посредлством запроса twitter.getHomeTimeline(), а теоретически он может вернуть коллекцию любого размера. Ну я то знаю что коллекция будет состоять из двух эллементов, но таблице это объяснить трудно, а вот data pipe такую штуку хавает свободно.
Где epoch, retweetCount, textOfTweets являються просто коллекциями из двух элементов с ожидаемыми результатами.
Погуглил оказывается для jUnit есть кастомный раннер аналогичный DataProvider аки TestNG
На этом пока все, перегруз инфы в одной статье то же не круто.
Про Geb будет в следующей части.