← Назад к вопросам

Какие знаешь инструменты для тестирования REST API?

2.0 Middle🔥 171 комментариев
#REST API и микросервисы#Тестирование

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Инструменты для тестирования REST API

Тестирование REST API — это критически важная часть разработки. Существует множество инструментов и подходов: unit тесты, интеграционные тесты, end-to-end тесты. Разберу самые популярные и практические.

1. Unit & Integration Тесты на Java

RestAssured (самый популярный для Java)

RestAssured — это Java DSL для тестирования REST сервисов. Написан Джейком Сковроским (создатель REST Assured).

import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;

public class UserAPITest {
    
    @BeforeEach
    public void setup() {
        RestAssured.baseURI = "http://localhost:8080";
    }
    
    @Test
    public void testGetUserSuccess() {
        RestAssured
            .given()
                .pathParam("userId", 123)
            .when()
                .get("/api/v1/users/{userId}")
            .then()
                .statusCode(200)
                .body("id", equalTo(123))
                .body("name", equalTo("John"))
                .body("email", notNullValue());
    }
    
    @Test
    public void testCreateUser() {
        RestAssured
            .given()
                .contentType(ContentType.JSON)
                .body("{\"name\":\"Jane\",\"email\":\"jane@example.com\"}")
            .when()
                .post("/api/v1/users")
            .then()
                .statusCode(201)
                .body("id", notNullValue())
                .body("name", equalTo("Jane"));
    }
    
    @Test
    public void testGetUsersWithFiltering() {
        RestAssured
            .given()
                .queryParam("status", "active")
                .queryParam("page", 1)
                .queryParam("size", 10)
            .when()
                .get("/api/v1/users")
            .then()
                .statusCode(200)
                .body("content.size()", greaterThan(0))
                .body("totalElements", greaterThan(0));
    }
    
    @Test
    public void testUpdateUser() {
        RestAssured
            .given()
                .pathParam("userId", 123)
                .contentType(ContentType.JSON)
                .body("{\"name\":\"Updated Name\"}")
            .when()
                .patch("/api/v1/users/{userId}")
            .then()
                .statusCode(200)
                .body("id", equalTo(123))
                .body("name", equalTo("Updated Name"));
    }
    
    @Test
    public void testDeleteUser() {
        RestAssured
            .given()
                .pathParam("userId", 123)
            .when()
                .delete("/api/v1/users/{userId}")
            .then()
                .statusCode(204);
    }
    
    @Test
    public void testErrorHandling() {
        RestAssured
            .given()
                .pathParam("userId", 999)
            .when()
                .get("/api/v1/users/{userId}")
            .then()
                .statusCode(404)
                .body("error", equalTo("User not found"))
                .body("status", equalTo(404));
    }
}

Плюсы:

  • Простая, понятная API
  • Цепочка методов (fluent)
  • Поддержка JSON Path для проверки ответов
  • Хорошая интеграция с JUnit
  • Много примеров в сети

Минусы:

  • Нужен реальный сервер (обычно локальный)
  • Медленнее чем mock тесты

Spring Test (для Spring приложений)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    public void testGetUserSuccess() throws Exception {
        mockMvc.perform(
            get("/api/v1/users/123")
                .contentType(MediaType.APPLICATION_JSON)
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", is(123)))
        .andExpect(jsonPath("$.name", is("John")));
    }
    
    @Test
    public void testCreateUser() throws Exception {
        String userJson = "{\"name\":\"Jane\",\"email\":\"jane@example.com\"}";
        
        mockMvc.perform(
            post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(userJson)
        )
        .andExpect(status().isCreated())
        .andExpect(jsonPath("$.id", notNullValue()))
        .andExpect(jsonPath("$.name", is("Jane")));
    }
}

Плюсы:

  • Не нужен реальный сервер
  • Быстрые тесты
  • Полная интеграция со Spring
  • MockMvc позволяет мокировать сервис слой

Минусы:

  • Только для Spring приложений
  • MockMvc тесты — это не совсем unit тесты

2. Тестирование с Mockito (мокирование зависимостей)

import org.mockito.Mockito;
import org.mockito.InjectMocks;
import org.mockito.Mock;

public class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;  // Зависимости будут мокированы
    
    @Test
    public void testGetUserById() {
        // Arrange
        User expectedUser = new User(1L, "John", "john@example.com");
        when(userRepository.findById(1L)).thenReturn(Optional.of(expectedUser));
        
        // Act
        User actualUser = userService.getUserById(1L);
        
        // Assert
        assertEquals(expectedUser.getId(), actualUser.getId());
        assertEquals(expectedUser.getName(), actualUser.getName());
        verify(userRepository, times(1)).findById(1L);
    }
    
    @Test
    public void testCreateUser() {
        // Arrange
        User newUser = new User("Jane", "jane@example.com");
        User savedUser = new User(2L, "Jane", "jane@example.com");
        when(userRepository.save(any(User.class))).thenReturn(savedUser);
        
        // Act
        User result = userService.createUser(newUser);
        
        // Assert
        assertNotNull(result.getId());
        assertEquals("Jane", result.getName());
        verify(userRepository).save(any(User.class));
    }
}

Плюсы:

  • Очень быстрые тесты (нет БД, нет сервера)
  • Можно тестировать edge cases
  • Полный контроль над behaviour зависимостей

Минусы:

  • Не тестирует реальное взаимодействие с БД
  • Может быть избыточным мокированием

3. WireMock (мокирование HTTP сервера)

WireMock позволяет создать фейковый HTTP сервер для тестирования.

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;

public class ExternalServiceClientTest {
    
    private WireMockServer wireMockServer;
    private ExternalServiceClient client;
    
    @BeforeEach
    public void setup() {
        wireMockServer = new WireMockServer(
            WireMockConfiguration.options()
                .port(8888)
                .bindAddress("localhost")
        );
        wireMockServer.start();
        
        client = new ExternalServiceClient("http://localhost:8888");
    }
    
    @AfterEach
    public void teardown() {
        wireMockServer.stop();
    }
    
    @Test
    public void testCallExternalAPI() {
        // Мокируем внешний API
        wireMockServer.stubFor(
            get(urlEqualTo("/api/users/123"))
                .willReturn(
                    aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody("{\"id\":123,\"name\":\"External User\"}")
                )
        );
        
        // Тестируем наш клиент
        User user = client.getUser(123);
        
        assertEquals(123, user.getId());
        assertEquals("External User", user.getName());
        
        // Проверяем, что запрос был сделан
        wireMockServer.verify(
            1,
            getRequestedFor(urlEqualTo("/api/users/123"))
        );
    }
    
    @Test
    public void testExternalAPITimeout() {
        wireMockServer.stubFor(
            get(urlEqualTo("/api/users/999"))
                .willReturn(
                    aResponse()
                        .withStatus(404)
                        .withBody("{\"error\":\"Not found\"}")
                )
        );
        
        assertThrows(NotFoundException.class, () -> {
            client.getUser(999);
        });
    }
}

Плюсы:

  • Мокирует реальный HTTP сервер
  • Тестирует клиент без зависимости от реального сервера
  • Может настраивать сложные сценарии (delays, errors)

Минусы:

  • Требует настройки в тестах
  • Может быть медленнее

4. Postman (Manual + Automation)

Postman — это GUI инструмент для тестирования и разработки API.

// Postman Test Script
pm.test("Status code is 200", function() {
    pm.response.to.have.status(200);
});

pm.test("Response has required fields", function() {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property('id');
    pm.expect(jsonData).to.have.property('name');
    pm.expect(jsonData).to.have.property('email');
});

pm.test("Response time is less than 1000ms", function() {
    pm.expect(pm.response.responseTime).to.be.below(1000);
});

// Сохранение данных для следующего запроса
var jsonData = pm.response.json();
pm.globals.set("userId", jsonData.id);

Плюсы:

  • Очень простой UI
  • Быстрое прототипирование
  • Хороший для manual тестирования
  • Встроенные примеры

Минусы:

  • Не подходит для CI/CD (нужна Newman)
  • Слабая версионизация (нужно хранить в Git)
  • Платные advanced функции

5. Newman (CLI для Postman)

# Запуск Postman коллекции из CLI
newman run collection.json

# С переменными окружения
newman run collection.json -e environment.json

# С отчётом
newman run collection.json --reporters cli,json --reporter-json-export report.json

# В Docker
docker run -v $(pwd):/etc/newman postman/newman run collection.json

6. TestNG (альтернатива JUnit для параметризованных тестов)

import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;

public class APIParametrizedTest {
    
    @DataProvider(name = "userIds")
    public Object[][] userIds() {
        return new Object[][] {
            { 1, "John" },
            { 2, "Jane" },
            { 3, "Bob" }
        };
    }
    
    @Test(dataProvider = "userIds")
    public void testGetUser(int userId, String expectedName) {
        RestAssured
            .given()
                .pathParam("userId", userId)
            .when()
                .get("/api/v1/users/{userId}")
            .then()
                .statusCode(200)
                .body("name", equalTo(expectedName));
    }
}

7. Cucumber BDD (для приёмочного тестирования)

Cucumber позволяет писать тесты на понятном языке (Given-When-Then).

# features/users.feature
Feature: User API
  Scenario: Get user by ID
    Given I have user ID 123
    When I call GET /api/v1/users/{userId}
    Then response status code should be 200
    And response should have user name "John"
    And response should have user email

  Scenario Outline: Get multiple users
    Given I have user ID <userId>
    When I call GET /api/v1/users/{userId}
    Then response status code should be <statusCode>
    And response should have user name "<name>"
    
    Examples:
      | userId | statusCode | name |
      | 1      | 200        | John |
      | 2      | 200        | Jane |
      | 999    | 404        |      |
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;

public class UserAPISteps {
    
    private int userId;
    private Response response;
    
    @Given("I have user ID {int}")
    public void setUserId(int id) {
        this.userId = id;
    }
    
    @When("I call GET /api/v1/users/{int}")
    public void callGetUser(int id) {
        response = RestAssured
            .given()
                .pathParam("userId", id)
            .when()
                .get("/api/v1/users/{userId}");
    }
    
    @Then("response status code should be {int}")
    public void checkStatusCode(int expectedCode) {
        assertEquals(expectedCode, response.statusCode());
    }
}

Плюсы:

  • Понятно для бизнеса (non-technical людям)
  • Хороший для acceptance тестирования
  • Living documentation

Минусы:

  • Может быть оверхеда для простых проектов
  • Требует extra steps layer

8. Karate (альтернатива Cucumber для API)

# features/users.feature
Feature: User Management API

  Scenario: Get user by ID
    Given url 'http://localhost:8080/api/v1'
    When method get
    And path 'users/123'
    Then status 200
    And match response.name == 'John'
    And match response.email != null
  
  Scenario: Create user
    Given url 'http://localhost:8080/api/v1'
    And path 'users'
    And request { name: 'Jane', email: 'jane@example.com' }
    When method post
    Then status 201
    And match response.id != null
    And def userId = response.id

Плюсы:

  • Простой синтаксис
  • Нет Java кода нужно
  • Хорошо для API тестирования

Минусы:

  • Менее популярная
  • Меньше примеров в сети

9. JMeter (для нагрузочного тестирования)

JMeter используется для performance и load тестирования.

<!-- JMeter Test Plan XML -->
<ThreadGroup guiclass="ThreadGroupGui">
  <elementProp name="ThreadGroup.main_controller" elementType="LoopController">
    <stringProp name="LoopController.loops">10</stringProp>
  </elementProp>
  <stringProp name="ThreadGroup.num_threads">50</stringProp>  <!-- 50 concurrent users -->
  <stringProp name="ThreadGroup.ramp_time">10</stringProp>    <!-- Ramp up over 10 seconds -->
</ThreadGroup>

<HTTPSampler guiclass="HttpTestSampleGui">
  <stringProp name="HTTPSampler.domain">localhost</stringProp>
  <stringProp name="HTTPSampler.port">8080</stringProp>
  <stringProp name="HTTPSampler.path">/api/v1/users</stringProp>
  <stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSampler>

Выбор инструмента: Матрица решений

СлучайРекомендуемый инструментПричина
Unit тесты сервисаMockito + JUnitБыстро, полный контроль
Integration тесты с БДSpring Test + TestContainersПолная интеграция, изолировано
API интеграционные тестыRestAssuredПростая и понятная API
Тесты внешних APIWireMockМокирование HTTP, воспроизводимость
Manual testingPostmanПростой UI, быстрое прототипирование
CI/CD автоматизацияNewman (Postman) или RestAssuredМожет запускаться из CLI
BDD acceptance тестыCucumber + RestAssuredПонятно для бизнеса
Performance тестыJMeterСпециализирован для нагрузки
API contract testingPactПроверка совместимости между сервисами

Best Practice: Тестовая пирамида для REST API

        /\
       /  \  E2E тесты (10%)
      /    \  Полный сценарий в production
     /______\
    /        \
   / Integration \ (30%)
  /    тесты     \  RestAssured, WireMock
 /________________\
/                  \
   Unit тесты (60%) 
Mockito, JUnit
\__________________/

Рекомендуемый стек для нового проекта

// Для unit тестов
Mockito + JUnit 5 + AssertJ

// Для integration тестов
Spring Test + TestContainers + RestAssured

// Для тестирования внешних сервисов
WireMock

// Для BDD
Cucumber + RestAssured

// Для performance
JMeter

Запомни: Unit тесты быстрые, интеграционные медленнее, E2E самые медленные. Пиши много unit тестов, достаточно интеграционных, и минимум E2E.