«1. Обзор

В этом кратком руководстве показано, как мы можем протестировать HTTP-API с отслеживанием состояния с помощью WireMock.

Чтобы начать работу с библиотекой, сначала ознакомьтесь с нашим руководством Introduction to WireMock.

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

Чтобы иметь возможность воспользоваться библиотекой WireMock, нам нужно включить в POM следующую зависимость:

<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock</artifactId>
    <version>2.21.0</version>
    <scope>test</scope>
</dependency>

3. Пример API, который мы хотим имитировать ~ ~~ Концепция сценариев в Wiremock заключается в том, чтобы помочь смоделировать различные состояния REST API. Это позволяет нам создавать тесты, в которых используемый API ведет себя по-разному в зависимости от состояния, в котором он находится.

Чтобы проиллюстрировать это, мы рассмотрим практический пример: сервис «Java Tip», который дает нам другую подсказку о Java всякий раз, когда мы запрашиваем ее конечную точку /java-tip.

Если мы попросим подсказку, мы получим ее в текстовом/простом виде:

Если мы позвоним еще раз, мы получим другую подсказку.

"use composition rather than inheritance"

4. Создание состояний сценария

Нам нужно заставить WireMock создать заглушки для конечной точки «/java-tip». Каждая заглушка будет возвращать определенный текст, соответствующий одному из 3 состояний фиктивного API:

В приведенном выше классе мы используем класс правил JUnit WireMock WireMockRule. Это настраивает сервер WireMock при запуске теста JUnit.

public class WireMockScenarioExampleIntegrationTest {
    private static final String THIRD_STATE = "third";
    private static final String SECOND_STATE = "second";
    private static final String TIP_01 = "finally block is not called when System.exit()" 
      + " is called in the try block";
    private static final String TIP_02 = "keep your code clean";
    private static final String TIP_03 = "use composition rather than inheritance";
    private static final String TEXT_PLAIN = "text/plain";
    
    static int port = 9999;
    
    @Rule
    public WireMockRule wireMockRule = new WireMockRule(port);    

    @Test
    public void changeStateOnEachCallTest() throws IOException {
        createWireMockStub(Scenario.STARTED, SECOND_STATE, TIP_01);
        createWireMockStub(SECOND_STATE, THIRD_STATE, TIP_02);
        createWireMockStub(THIRD_STATE, Scenario.STARTED, TIP_03);
        
    }

    private void createWireMockStub(String currentState, String nextState, String responseBody) {
        stubFor(get(urlEqualTo("/java-tip"))
          .inScenario("java tips")
          .whenScenarioStateIs(currentState)
          .willSetStateTo(nextState)
          .willReturn(aResponse()
            .withStatus(200)
            .withHeader("Content-Type", TEXT_PLAIN)
            .withBody(responseBody)));
    }

}

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

Основные методы, используемые при создании заглушек:

whenScenarioStateIs: определяет, в каком состоянии должен находиться сценарий, чтобы WireMock мог использовать эту заглушку. willSetStateTo: задает значение, которое WireMock устанавливает в состояние после того, как эта заглушка была использована.

    Начальное состояние любого сценария — Scenario.STARTED. Итак, мы создаем заглушку, которая используется, когда состояние Scenario.STARTED. Это переводит состояние в SECOND_STATE.

Мы также добавляем заглушки для перехода из SECOND_STATE в THIRD_STATE и, наконец, из THIRD_STATE обратно в Scenario.STARTED. Таким образом, если мы продолжаем вызывать конечную точку /java-tip, состояние изменяется следующим образом:

Сценарий.STARTED -\u003e SECOND_STATE -\u003e THIRD_STATE -\u003e Scenario.STARTED

5. Использование сценария

В сценарии WireMock мы просто делаем повторные вызовы конечной точки /java-tip. Поэтому нам нужно изменить наш тестовый класс следующим образом:

Метод nextTip() вызывает конечную точку /java-tip и затем возвращает ответ в виде строки. Поэтому мы используем это в каждом вызове assertEquals(), чтобы проверить, что вызовы действительно заставляют сценарий циклически перемещаться по различным состояниям.

    @Test
    public void changeStateOnEachCallTest() throws IOException {
        createWireMockStub(Scenario.STARTED, SECOND_STATE, TIP_01);
        createWireMockStub(SECOND_STATE, THIRD_STATE, TIP_02);
        createWireMockStub(THIRD_STATE, Scenario.STARTED, TIP_03);

        assertEquals(TIP_01, nextTip());
        assertEquals(TIP_02, nextTip());
        assertEquals(TIP_03, nextTip());
        assertEquals(TIP_01, nextTip());        
    }

    private String nextTip() throws ClientProtocolException, IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet request = new HttpGet(String.format("http://localhost:%s/java-tip", port));
        HttpResponse httpResponse = httpClient.execute(request);
        return firstLineOfResponse(httpResponse);
    }

    private static String firstLineOfResponse(HttpResponse httpResponse) throws IOException {
        try (BufferedReader reader = new BufferedReader(
          new InputStreamReader(httpResponse.getEntity().getContent()))) {
            return reader.readLine();
        }
    }

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

В этой статье мы увидели, как использовать сценарии WireMock для имитации API, который меняет свой ответ в зависимости от состояния, в котором он находится.

Как всегда, весь код, использованный в этой учебник доступен на GitHub.

«