Рубрики
Без рубрики

Тестирование REST API с огурцом

Быстрое тестирование REST API с огурцом.

Автор оригинала: baeldung.

1. Обзор

В этом учебнике дается введение в Cucumber , широко используемый инструмент для приемочного тестирования пользователей, и как использовать его в тестах REST API.

Кроме того, чтобы сделать статью самодостаточной и независимой от каких-либо внешних служб REST, мы будем использовать WireMock, библиотеку веб-сервисов, использующих stubbing и mocking. Если вы хотите узнать больше об этой библиотеке, пожалуйста, обратитесь к введению в WireMock .

2. Корнишон – Язык огурца

Cucumber-это платформа тестирования , которая поддерживает Behavior Driven Development (BDD) , позволяя пользователям определять операции приложения в виде обычного текста. Он работает на основе Gherkin Domain Specific Language (DSL). Этот простой, но мощный синтаксис корнишона позволяет разработчикам и тестировщикам писать сложные тесты, сохраняя его понятным даже для нетехнических пользователей.

2.1. Введение в корнишон

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

Вся структура должна быть записана в файл с расширением feature , который будет распознан Cucumber.

Вот простой пример документа из корнишона:

Feature: A short description of the desired functionality

  Scenario: A business situation
    Given a precondition
    And another precondition
    When an event happens
    And another event happens too
    Then a testable outcome is achieved
    And something else is also completed

В следующих разделах мы опишем несколько наиболее важных элементов в структуре корнишонов.

2.2. Характеристика

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

Парсер огурцов пропускает весь текст, за исключением ключевого слова Feature , и включает его только в целях документации.

2.3. Сценарии и шаги

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

Эти действия выполняются с помощью шагов, определенных одним из пяти ключевых слов: Дано , Когда , Тогда , И , и Но|/.

  • Данный : Этот шаг должен привести систему в четко определенное состояние, прежде чем пользователи начнут взаимодействовать с приложением. Предложение Given можно считать предварительным условием для варианта использования.
  • When : Шаг When используется для описания события, которое происходит с приложением. Это может быть действие, предпринятое пользователями, или событие, вызванное другой системой.
  • Затем : Этот шаг предназначен для указания ожидаемого результата теста. Результат должен быть связан с бизнес-ценностями тестируемой функции.
  • И и Но : Эти ключевые слова можно использовать для замены приведенных выше ключевых слов шага, когда существует несколько шагов одного и того же типа.

Огурец на самом деле не различает эти ключевые слова, однако они все еще существуют, чтобы сделать функцию более читаемой и согласованной со структурой BDD.

3. Реализация Cucumber-JVM

Cucumber изначально был написан на Ruby и был портирован на Java с реализацией Cucumber-JVM, которая является предметом этого раздела.

3.1. Зависимости Maven

Чтобы использовать Cucumber-JVM в проекте Maven, в POM необходимо включить следующую зависимость:


    io.cucumber
    cucumber-java
    6.8.0
    test

Чтобы облегчить тестирование JUnit с огурцом, нам нужно иметь еще одну зависимость:


    io.cucumber
    cucumber-junit
    6.8.0

В качестве альтернативы мы можем использовать другой артефакт, чтобы воспользоваться преимуществами лямбда-выражений в Java 8, которые не будут рассмотрены в этом руководстве.

3.2. Определения шагов

Сценарии корнишонов были бы бесполезны, если бы они не были переведены в действия, и именно здесь в игру вступают определения шагов. По сути, определение шага-это аннотированный метод Java с прикрепленным шаблоном, задача которого заключается в преобразовании шагов корнишона в виде обычного текста в исполняемый код. После анализа документа функции Cucumber будет искать определения шагов, которые соответствуют предопределенным шагам корнишона для выполнения.

Чтобы сделать это более ясным, давайте рассмотрим следующий шаг:

Given I have registered a course in Baeldung

И определение шага:

@Given("I have registered a course in Baeldung")
public void verifyAccount() {
    // method implementation
}

Когда Огурец читает данный шаг, он будет искать определения шагов, шаблоны аннотирования которых соответствуют тексту корнишона.

4. Создание и запуск тестов

4.1. Написание файла функций

Давайте начнем с объявления сценариев и шагов в файле с именем, заканчивающимся расширением .feature :

Feature: Testing a REST API
  Users should be able to submit GET and POST requests to a web service, 
  represented by WireMock

  Scenario: Data Upload to a web service
    When users upload data on a project
    Then the server should handle it and return a success status

  Scenario: Data retrieval from a web service
    When users want to get information on the 'Cucumber' project
    Then the requested data is returned

Теперь мы сохраняем этот файл в каталоге с именем Feature при условии , что каталог будет загружен в путь к классу во время выполнения, например src/main/resources .

4.2. Настройка JUnit для работы с Огурцом

Для того, чтобы JUnit знал о Cucumber и читал файлы функций при запуске, класс Cucumber должен быть объявлен как Runner . Нам также нужно указать JUnit место для поиска файлов функций и определений шагов.

@RunWith(Cucumber.class)
@CucumberOptions(features = "classpath:Feature")
public class CucumberIntegrationTest {
    
}

Как вы можете видеть, элемент features параметра Cucumber находит файл функций, созданный ранее. Еще один важный элемент, называемый glue , предоставляет пути к определениям шагов. Однако, если определения тестового случая и шага находятся в том же пакете, что и в этом руководстве, этот элемент может быть удален.

4.3. Написание определений шагов

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

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

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

@When("users upload data on a project")
public void usersUploadDataOnAProject() throws IOException {
    
}

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

@When("users want to get information on the {string} project")
public void usersGetInformationOnAProject(String projectName) throws IOException {
    
}

Как вы можете видеть, пользователи Получают информацию О проекте метод принимает аргумент String , который является именем проекта. Этот аргумент объявлен {string} в аннотации, и здесь он соответствует Cucumber в тексте шага.

В качестве альтернативы мы могли бы использовать регулярное выражение:

@When("^users want to get information on the '(.+)' project$")
public void usersGetInformationOnAProject(String projectName) throws IOException {
    
}

Обратите внимание, что ‘^’ и ‘$’ , которые указывают начало и конец регулярного выражения соответственно. В то время как ‘(.+)’ соответствует параметру String .

Мы предоставим рабочий код для обоих вышеперечисленных методов в следующем разделе.

4.4. Создание и выполнение тестов

Во-первых, мы начнем со структуры JSON, чтобы проиллюстрировать данные, загруженные на сервер по запросу POST и загруженные клиенту с помощью GET. Эта структура сохраняется в поле json String и показана ниже:

{
    "testing-framework": "cucumber",
    "supported-language": 
    [
        "Ruby",
        "Java",
        "Javascript",
        "PHP",
        "Python",
        "C++"
    ],

    "website": "cucumber.io"
}

Для демонстрации REST API мы используем сервер WireMock:

WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());

Кроме того, мы будем использовать API Apache HttpClient для представления клиента, используемого для подключения к серверу:

CloseableHttpClient httpClient = HttpClients.createDefault();

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

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

wireMockServer.start();

Использование API WireMock для заглушения службы REST:

configureFor("localhost", wireMockServer.port());
stubFor(post(urlEqualTo("/create"))
  .withHeader("content-type", equalTo("application/json"))
  .withRequestBody(containing("testing-framework"))
  .willReturn(aResponse().withStatus(200)));

Теперь отправьте запрос POST с содержимым, взятым из поля json String , объявленного выше, на сервер:

HttpPost request = new HttpPost("http://localhost:" + wireMockServer.port() + "/create");
StringEntity entity = new StringEntity(jsonString);
request.addHeader("content-type", "application/json");
request.setEntity(entity);
HttpResponse response = httpClient.execute(request);

Следующий код утверждает, что запрос POST был успешно получен и обработан:

assertEquals(200, response.getStatusLine().getStatusCode());
verify(postRequestedFor(urlEqualTo("/create"))
  .withHeader("content-type", equalTo("application/json")));

Сервер должен остановиться после использования:

wireMockServer.stop();

Второй метод, который мы реализуем здесь, – это пользователи Получают Информацию О Проекте(Строка Имя проекта ) . Как и в первом тесте, нам нужно запустить сервер, а затем заглушить службу REST:

wireMockServer.start();

configureFor("localhost", wireMockServer.port());
stubFor(get(urlEqualTo("/projects/cucumber"))
  .withHeader("accept", equalTo("application/json"))
  .willReturn(aResponse().withBody(jsonString)));

Отправка запроса GET и получение ответа:

HttpGet request = new HttpGet("http://localhost:" + wireMockServer.port() + "/projects/" + projectName.toLowerCase());
request.addHeader("accept", "application/json");
HttpResponse httpResponse = httpClient.execute(request);

Мы преобразуем переменную http-ответа в Строку с помощью вспомогательного метода:

String responseString = convertResponseToString(httpResponse);

Вот реализация этого вспомогательного метода преобразования:

private String convertResponseToString(HttpResponse response) throws IOException {
    InputStream responseStream = response.getEntity().getContent();
    Scanner scanner = new Scanner(responseStream, "UTF-8");
    String responseString = scanner.useDelimiter("\\Z").next();
    scanner.close();
    return responseString;
}

Следующее проверяет весь процесс:

assertThat(responseString, containsString("\"testing-framework\": \"cucumber\""));
assertThat(responseString, containsString("\"website\": \"cucumber.io\""));
verify(getRequestedFor(urlEqualTo("/projects/cucumber"))
  .withHeader("accept", equalTo("application/json")));

Наконец, остановите сервер, как описано ранее.

5. Параллельное выполнение функций

Cucumber-JVM изначально поддерживает параллельное выполнение тестов в нескольких потоках. Мы будем использовать JUnit вместе с плагином Maven Failsafe для выполнения бегунов. В качестве альтернативы мы могли бы использовать Maven Surefire.

JUnit запускает файлы функций параллельно, а не сценарии, что означает, что все сценарии в файле функций будут выполняться одним и тем же потоком .

Теперь давайте добавим конфигурацию плагина:


    maven-failsafe-plugin
    ${maven-failsafe-plugin.version}
    
        
            CucumberIntegrationTest.java
        
        methods
        2
    
    
        
            
                integration-test
                verify
            
        
    

Обратите внимание, что:

  • параллельный: может быть классами, методами или и тем , и другим – в нашем случае классы заставят каждый тестовый класс запускаться в отдельном потоке
  • количество потоков: указывает, сколько потоков должно быть выделено для этого выполнения

Это все, что нам нужно сделать, чтобы запустить функции Cucumber параллельно.

6. Заключение

В этом уроке мы рассмотрели основы Cucumber и то, как этот фреймворк использует доменный язык Gherkin для тестирования REST API.

Как обычно, все примеры кода, показанные в этом руководстве, доступны на GitHub .