Тестирование API REST с JBehave

Тестирование API REST с JBehave

1. Введение

В этой статье мы кратко рассмотримJBehave, а затем сосредоточимся на тестировании REST API с точки зрения BDD.

2. JBehave и BDD

JBehave - это основанная на поведении среда разработки. Он намерен предоставить интуитивно понятный и доступный способ автоматического приемочного тестирования.

Если вы не знакомы с BDD, рекомендуется начать сthis article, covering on another BDD testing framework – Cucumber, в котором мы представляем общую структуру и функции BDD.

Как и в других средах BDD, JBehave принимает следующие концепции:

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

  • Сценарии - представляют собой конкретные примеры поведения системы

  • Шаги - представляют фактическое поведение с использованием классических ключевых слов BDD:Given,When иThen

Типичный сценарий будет:

Given a precondition
When an event occurs
Then the outcome should be captured

Каждый шаг в сценарии соответствует аннотации в JBehave:

  • @Given: инициировать контекст

  • @When: выполнить действие

  • @Then: проверить ожидаемый результат

3. Maven Dependency

Чтобы использовать JBehave в нашем проекте maven, зависимостьjbehave-core должна быть включена вpom:


    org.jbehave
    jbehave-core
    4.1
    test

4. Быстрый пример

Чтобы использовать JBehave, нам нужно выполнить следующие шаги:

  1. Напишите историю пользователя

  2. Сопоставить шаги из пользовательской истории с кодом Java

  3. Настроить пользовательские истории

  4. Запустите JBehave тесты

  5. Обзор результатов

4.1. История

Начнем со следующей простой истории: «как пользователь я хочу увеличить счетчик, чтобы значение счетчика увеличилось на 1».

Мы можем определить историю в файле.story:

Scenario: when a user increases a counter, its value is increased by 1

Given a counter
And the counter has any integral value
When the user increases the counter
Then the value of the counter must be 1 greater than previous value

4.2. Отображение шагов

Учитывая шаги, давайте реализуем это на Java:

public class IncreaseSteps {
    private int counter;
    private int previousValue;

    @Given("a counter")
    public void aCounter() {
    }

    @Given("the counter has any integral value")
    public void counterHasAnyIntegralValue() {
        counter = new Random().nextInt();
        previousValue = counter;
    }

    @When("the user increases the counter")
    public void increasesTheCounter() {
        counter++;
    }

    @Then("the value of the counter must be 1 greater than previous value")
    public void theValueOfTheCounterMustBe1Greater() {
        assertTrue(1 == counter - previousValue);
    }
}

Помните, чтоthe value in the annotations must accurately match the description.

4.3. Настройка нашей истории

Для выполнения шагов нам нужно подготовить почву для нашей истории:

public class IncreaseStoryLiveTest extends JUnitStories {

    @Override
    public Configuration configuration() {
        return new MostUsefulConfiguration()
          .useStoryLoader(new LoadFromClasspath(this.getClass()))
          .useStoryReporterBuilder(new StoryReporterBuilder()
            .withCodeLocation(codeLocationFromClass(this.getClass()))
            .withFormats(CONSOLE));
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(), new IncreaseSteps());
    }

    @Override
    protected List storyPaths() {
        return Arrays.asList("increase.story");
    }

}

ВstoryPaths() мы предоставляем путь к файлу.story для анализа JBehave. Фактическая реализация шагов представлена ​​вstepsFactory(). Затем вconfiguration() загрузчик историй и отчет истории настроены правильно.

Теперь, когда у нас все готово, мы можем начать наш рассказ, просто запустив:mvn clean test.

4.4. Просмотр результатов теста

Мы можем увидеть наш результат теста в консоли. Поскольку наши тесты прошли успешно, результат будет таким же, как в нашей истории:

Scenario: when a user increases a counter, its value is increased by 1
Given a counter
And the counter has any integral value
When the user increases the counter
Then the value of the counter must be 1 greater than previous value

Если мы забудем реализовать какой-либо шаг сценария, отчет сообщит нам. Допустим, мы не реализовали шаг@When:

Scenario: when a user increases a counter, its value is increased by 1
Given a counter
And the counter has any integral value
When the user increases the counter (PENDING)
Then the value of the counter must be 1 greater than previous value (NOT PERFORMED)
@When("the user increases the counter")
@Pending
public void whenTheUserIncreasesTheCounter() {
    // PENDING
}

В отчете будет указано, что шаг@When ожидает выполнения, и из-за этого шаг@Then не будет выполнен.

Что если наш шаг @Then завершится неудачей? Мы можем определить ошибку сразу же из отчета:

Scenario: when a user increases a counter, its value is increased by 1
Given a counter
And the counter has any integral value
When the user increases the counter
Then the value of the counter must be 1 greater than previous value (FAILED)
(java.lang.AssertionError)

5. Тестирование REST API

Теперь мы поняли основыJBhave; мы увидим, как протестировать REST API с его помощью. Наши тесты будут основаны на нашемprevious article discussing how to test REST API with Java.

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

5.1. Проверка кода состояния

История:

Scenario: when a user checks a non-existent user on github, github would respond 'not found'

Given github user profile api
And a random non-existent username
When I look for the random user via the api
Then github respond: 404 not found

When I look for eugenp1 via the api
Then github respond: 404 not found

When I look for eugenp2 via the api
Then github respond: 404 not found

Шаги:

public class GithubUserNotFoundSteps {

    private String api;
    private String nonExistentUser;
    private int githubResponseCode;

    @Given("github user profile api")
    public void givenGithubUserProfileApi() {
        api = "https://api.github.com/users/%s";
    }

    @Given("a random non-existent username")
    public void givenANonexistentUsername() {
        nonExistentUser = randomAlphabetic(8);
    }

    @When("I look for the random user via the api")
    public void whenILookForTheUserViaTheApi() throws IOException {
        githubResponseCode = getGithubUserProfile(api, nonExistentUser)
          .getStatusLine()
          .getStatusCode();
    }

    @When("I look for $user via the api")
    public void whenILookForSomeNonExistentUserViaTheApi(
      String user) throws IOException {
        githubResponseCode = getGithubUserProfile(api, user)
          .getStatusLine()
          .getStatusCode();
    }

    @Then("github respond: 404 not found")
    public void thenGithubRespond404NotFound() {
        assertTrue(SC_NOT_FOUND == githubResponseCode);
    }

    //...
}

Обратите внимание, как в реализации шаговwe used the parameter injection feature. Аргументы, извлеченные из кандидата шага, просто соответствуют в естественном порядке параметрам в аннотированном методе Java.

Также поддерживаются аннотированные именованные параметры:

@When("I look for $username via the api")
public void whenILookForSomeNonExistentUserViaTheApi(
  @Named("username") String user) throws IOException

5.2. Проверка типа носителя

Вот простая история тестирования MIME-типа:

Scenario: when a user checks a valid user's profile on github, github would respond json data

Given github user profile api
And a valid username
When I look for the user via the api
Then github respond data of type json

И вот шаги:

public class GithubUserResponseMediaTypeSteps {

    private String api;
    private String validUser;
    private String mediaType;

    @Given("github user profile api")
    public void givenGithubUserProfileApi() {
        api = "https://api.github.com/users/%s";
    }

    @Given("a valid username")
    public void givenAValidUsername() {
        validUser = "eugenp";
    }

    @When("I look for the user via the api")
    public void whenILookForTheUserViaTheApi() throws IOException {
        mediaType = ContentType
          .getOrDefault(getGithubUserProfile(api, validUser).getEntity())
          .getMimeType();
    }

    @Then("github respond data of type json")
    public void thenGithubRespondDataOfTypeJson() {
        assertEquals("application/json", mediaType);
    }
}

5.3. Тестирование полезной нагрузки JSON

Тогда последний рассказ:

Scenario: when a user checks a valid user's profile on github, github's response json should include a login payload with the same username

Given github user profile api
When I look for eugenp via the api
Then github's response contains a 'login' payload same as eugenp

И простые прямые шаги реализации:

public class GithubUserResponsePayloadSteps {

    private String api;
    private GitHubUser resource;

    @Given("github user profile api")
    public void givenGithubUserProfileApi() {
        api = "https://api.github.com/users/%s";
    }

    @When("I look for $user via the api")
    public void whenILookForEugenpViaTheApi(String user) throws IOException {
        HttpResponse httpResponse = getGithubUserProfile(api, user);
        resource = RetrieveUtil.retrieveResourceFromResponse(httpResponse, GitHubUser.class);
    }

    @Then("github's response contains a 'login' payload same as $username")
    public void thenGithubsResponseContainsAloginPayloadSameAsEugenp(String username) {
        assertThat(username, Matchers.is(resource.getLogin()));
    }
}

6. Резюме

В этой статье мы кратко представили JBehave и реализовали тесты REST API в стиле BDD.

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

Как всегда, пример кода можно найти вthe Github project.