Какие знаешь инструменты для тестирования REST API?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Инструменты для тестирования 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 |
| Тесты внешних API | WireMock | Мокирование HTTP, воспроизводимость |
| Manual testing | Postman | Простой UI, быстрое прототипирование |
| CI/CD автоматизация | Newman (Postman) или RestAssured | Может запускаться из CLI |
| BDD acceptance тесты | Cucumber + RestAssured | Понятно для бизнеса |
| Performance тесты | JMeter | Специализирован для нагрузки |
| API contract testing | Pact | Проверка совместимости между сервисами |
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.