В сообществе тестировщиков считается общепризнанным проводить функциональное тестирование с использованием множества библиотек и фреймворков, которые фокусируются только на UI. Selenium, комбинируемый с несколькими языками программирования, с помощью которого проверяются функциональные изменения, кажется сейчас довольно популярным. Этот процесс определенно отнимает много времени, потому что во время прогонки тестов открывается браузер для выполнения действий в пользовательском интерфейсе. Если мы делаем тесты в нескольких браузерах, количество времени, необходимое для запуска браузера, увеличивается. Более того, при написании тест-кейсов мы прописываем XPath-ы или CSS-селекторы для поиска необходимых веб-элементов, и, например, в браузерах IE и Edge они будут вести себя несколько иначе, нежели чем в Chrome или Firefox.

Более того, помня о пирамиде тестирования, мы должны учитывать количество тестов, выполняемых на каждом из уровней.

Процесс автоматизации тестирования рассматривается как довольно медленная проверка приложения через его пользовательский интерфейс, что в свою очередь увеличивает время сборки. С точки зрения CI/CD это не кажется целесообразным подходом. Не делайте поспешных выводов — я не пытаюсь донести, что проверка UI не важна и что она не стоит затраченных ресурсов.

Это на самом деле важно, но требует выполнения другого типа тестов на другом уровне, чтобы снизить количество UI-тестов до минимально возможного количества. Этот подход позволяет нам внедрить понятие идеальной тестовой пирамиды в наш проект. UI-тесты хороши для регрессионного и приемочного тестирования, но есть и другие тесты, которые обеспечивают более быструю и качественную обратную связь о состоянии приложения для команды разработки.

Если мы хотим, чтобы петля обратной связи была как можно короче, нам необходимо выбрать быстрые тесты, которые приносят наибольшую ценность. Вместо выполнения большого количества E2E UI-тестов, которые в большинстве случаев довольно медленные, лучше попробовать перенести тесты на уровень API и использовать UI-тесты только для проверки корректности UI. Такой подход помогает сэкономить массу времени при разработке и тестировании проекта. Например, в одном из проектов, над которым я работал, прогон E2E UI-тестов занимал всего 15 минут (после того, как сократили эту группу тестов). Когда я покрыл сокращенные тестовые сценарии на уровне API, время исполнения тестов сократилось до трех минут. Есть множество фреймворков тестирования API, которые позволяют провести тестирование API. Можно использовать Postman и быстро автоматизировать коллекции, которые обычно предоставляются разработчиками в качестве документации. Можно использовать простую библиотеку HTTP-клиента и создать свою собственную среду тестирования для тестирования API. Или же можно использовать существующие библиотеки, например, REST Assured и и затем написать собственный тестовый фреймворк. Использование REST Assured имеет большое преимущество — это готовая к использованию библиотека, которая имеет поддержку большого сообщества, и с ее помощью можно создать среду под проектные нужды. Это менее времязатратный способ, чем написать целый фреймворк с нуля. Кроме того, эта среда может использоваться в других проектах внутри компании.

В статье я не буду фокусироваться на основах REST Assured, а поделюсь полезными приемами и решениями, которые ускорят процесс написания тестов. Получить основную информацию по REST Assured можно на официальной странице.

Советы по написанию тестов

REST Assured позволяет пользователям обрабатывать HTTP-запросы, которые можно записать в достаточно читабельном формате. Ниже написан простой тест, с помощью которого можно проверить, получаем ли мы код состояния 200 на определенный GET-запрос.

Принимая во внимание только простые параметры, такие как ограничение на отображение количества статей или проверка кода ответа, выше написанный тест-кейс кажется простым.

Создание POJO / обертки 

Однако, что случается, когда количество тест-кейсов увеличивается и мы хотим протестировать что-то гораздо более сложное, нежели чем просто обычный код ответа?

Лучшей идеей будет ввести обертку для методов HTTP-запроса. Это намного упрощает написание кода и дает возможность переиспользования кода. Переиспользование методов помогает писать тесты быстрее, и, что важнее, с их помощью код будет более читабельным и поддерживаемым.

private Response addUser(String path, Object bodyData) {
    return given()
            .spec(specification.build())
            .body(bodyData)
            .when()
            .post(path)
            .then()
            .extract()
            .response();
}
private <T> T getResourceData(String path, Class<T> responseClass) {
return given()
.spec(specification.build())
.when()
.get(path)
.then()
.statusCode(200)
.extract()
.as(responseClass);
}

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

В методе addUser() мы создаем пользователя. В этом методе мы передаем данные пользователя, чтобы создать его в базе данных. Есть много способов создать данные и передать их с помощью определенного запроса в теле запроса. Можно создать HashMap и использовать его для создания тела запроса. Другой вариант — создать подходящий JSON-парсер и отправить данные в формате JSON.

Использование Lombok

По моему мнению, самый лучший и быстрый способ — это создание POJO-объектов. Это делает код чистым и годным для переиспользования. С помощью плагина Lombok, который можно подключить к IntelliJ, создание таких объектов происходит очень просто.

Чтобы это сделать, необходимо добавить несколько аннотаций:

@Setter
@Getter
@Accessors(fluent = true)
public class User {
 
    private String username;
    private String email;
    private String password;
     
}

Lombok сам генерирует сеттеры, поэтому вам не надо тратить время на написание корректных сеттеров и геттеров. Добавление аннотации @Accessors() помогает нам создать fluent-сеттер, который делает код более читабельным.

@Test
public void shouldCreateUser() {
     
    User newUser = new User();
    newUser.username("Joe Doe")
            .password(System.getenv("JOE"))
            .email("joe.doe@siili.com");
 
    addUser(newUser);
 
}
private Response addUser(Object bodyData) {
    return given()
            .spec(specification.build())
            .body(bodyData)
            .when()
            .post(EndPoints.USEREDATA)
            .then()
            .extract()
            .response();
}

Наши тестовые методы выглядят довольно чисто и емко. Мы создали объект «пользователь» и передали его в общий метод, который мы создали ранее. 

Помните о том, что пароль не передается в качестве читаемого параметра. Это значение берется из переменных окружения, что делает наш тест более надежным.

Для лучшей удобочитаемости и поддержки кода, я также создал перечисление Enum EndPoints, в котором хранятся URI paths. Я также немного изменил код. Следовательно, если мы не используем любой другой эндпоинт в методе addUser(), мы можем скрыть параметр path, так что наш код будет выглядеть как в примере выше.

Использование AssertJ

Давайте вернемся к нашему тестовому методу. В данном случае он делает не что иное, как создает пользователя в качестве объекта и передает его в эндпоинт. Более того, он не проверяет, был ли запрос выполнен с какой-либо ошибкой или успешно, или нет.

В REST Assured есть встроенные утверждения, которые позволяют нам проверить, например, был ли ожидаемый код состояния корректным.

Однако, я бы рекомендовал использовать библиотеку AssertJ для всех видов assert-ов, потому что она кажется простой в использовании. AssertJ включает множество методов, полезных для создания assert-ов.

С AssertJ наш тестовый метод будет выглядеть как в примере ниже:

@Test
public void shouldCreateUser() {
 
    User newUser = new User();
    newUser.username("Joe Doe")
            .password(System.getenv("JOE"))
            .email("joe.doe@siili.com");
 
    addUser(newUser);
    User requestUser = getResourceData(EndPoints.USEREDATA, User.class);
 
    assertThat(requestUser.username()).contains(newUser.username());
    assertThat(requestUser.email()).contains(newUser.email());
}

AssertJ включает множество полезных методов, проверяющих необходимые данные в удобном виде. Правильный обработчик записи логов может позволить добавлять информацию в логи о том, что ассерт сфейлил (без отображения сложного стек трейса). 

Эти три простых приема помогли мне ускорить написание тестов в проекте, в котором я участвовал. Существует множество других вещей, которые могут помочь в написании тестов, тем не менее, все зависит от ваших текущих потребностей, вида тестов и самого проекта.

Категоризация тестов

Старайтесь не писать тест-кейсы в одном тестовом классе. Вам может показаться хорошим решением держать тест-кейсы в одном месте. Однако, можно столкнуться с такой проблемой, когда есть огромный список тестовых сценариев, которые надо покрыть, и вам нужно найти один конкретный тест в одном классе.

Разработчики, с которыми вы вместе работаете на проекте, скорее всего, хранят свою документацию в инструментах наподобие Postman или Swagger при работе над созданием API. Для поиска нужного эндпоинта каждая коллекция эндпоинтов должна быть разделена на функциональные категории.  

Попробуйте сделать то же самое со своим тестом — сгруппируйте их по категориям и держите каждую функциональную категорию в отдельном классе тестов.

Создание наглядных отчетов о тестировании

Нет ничего хуже, чем просматривать плохо написанные логи или вообще их отсутствие. В этом случае отладка становится кошмаром. Чтобы облегчить жизнь и сэкономить себе время, раздумывая об ошибках в коде и тестах, попробуйте описать каждый выполняемый шаг. Особенно, когда коллекция тестов растет. Если вы предпочитаете делать простые текстовые отчеты после каждого выполнения тестов, я рекомендую добавить логгер Apache Log4j в код. Если же вы любите наглядные отчеты о тестах с разными графиками, иллюстрирующими прохождения и падения тестов, я предлагаю обратить внимание на библиотеку Extend Report.

Это того стоит?

Одним из преимуществ тестирования API является возможность проверки работы приложения даже без готового UI. Так, это помогает дать быструю обратную связь команде разработке или представителям бизнеса о текущем качестве приложения. Найденные ошибки или баги могут быть исправлены быстрее — поскольку правятся на ранней стадии разработки.

API-тесты, выполняемые вручную или автоматически, являются более быстрыми, чем UI-тесты. Тестировщику не обязательно проходить по UI целиком, чтобы проверить функциональность. Ответы на запросы приходят быстрее, поскольку UI не включен в процесс.

Хотя, тестирование API с помощью REST Assured обладает множеством как преимуществ, так и недостатков.

Валидация всех параметров в пределах запроса может быть непростой. Нельзя проверить только код состояния и на 100% быть уверенным в том, что все работает корректно. Необходимо убедиться в том, что все данные параметров используют правильный строковый или числовой тип данных и проходят по другим критериям проверки.

Зачастую вызовы API должны появляться в определенном порядке для правильной работы. Для тестировщика, который до сих пор работал над другим видом тестов, это может быть сложно.

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

Заключение

Во время тестирования API можно проверить работу приложения на разных уровнях. Написанные тест-кейсы могут быть использованы не только для функционального тестирования, но также и для тестирования безопасности, производительности и нагрузочного тестирования. С помощью библиотеки REST Assured тесты можно написать быстрее, а баги обнаружить на более ранней стадии разработки. Тем не менее, нужно помнить о том, что API тесты и UI тесты — это не единственные виды тестов, которыми должна заниматься команда тестирования, чтобы обеспечить высокое качество приложения. Есть и другие уровни, которые отображены в пирамиде тестирования, и вашей команде следует, например, фокусироваться также на юнит- и интеграционных тестах. 

Подведем краткий итог всему вышесказанному:

  • Старайтесь содержать тест-кейсы в порядке.

  • Группируйте тест-кейсы по функциональным категориям.

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

  • Используйте библиотеку AssertJ для проверки данных или любую другую, которую вы предпочитаете.

  • Попробуйте обернуть методы, которые можно переиспользовать в других тест-кейсах, сделайте что-то вроде Page Object-ов в UI-тестировании.

  • Используйте плагин Lombok для создания сеттеров, геттеров и fluent-сеттеров.


Всех, кто прочитал статью до конца, приглашаем на открытое занятие в Otus, на котором мы рассмотрим тестирование API с помощью RestAsured и JsonSchemaValidator. Регистрируйтесь по ссылке.

Комментарии (0)