Тесты: @QuarkusTest и RestAssured
Проверять API вручную через curl после каждого изменения утомительно. Тест делает то же самое автоматически: отправляет запросы и сравнивает ответы с ожидаемыми. В Quarkus тест с аннотацией @QuarkusTest запускает настоящее приложение — с базой, бинами и настройками профиля test, — а запросы к нему удобно писать библиотекой RestAssured. Обе зависимости генератор уже добавил в pom.xml: quarkus-junit и rest-assured.
Первый тест
Тесты лежат в src/test/java в том же пакете, что и код:
// src/test/java/com/example/blog/PostResourceTest.java
package com.example.blog;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@QuarkusTest
class PostResourceTest {
@Test
void listsPosts() {
given()
.when().get("/posts")
.then()
.statusCode(200)
.body("size()", greaterThanOrEqualTo(2));
}
@Test
void createsPost() {
given()
.contentType(ContentType.JSON)
.body(new NewPost("Тестовый пост", "Написан тестом"))
.when().post("/posts")
.then()
.statusCode(201)
.body("id", notNullValue())
.body("title", is("Тестовый пост"));
}
@Test
void returns404ForUnknownPost() {
given()
.when().get("/posts/999")
.then()
.statusCode(404)
.body("error", is("Пост 999 не найден"));
}
}Цепочка читается как фраза: «дано — когда GET /posts — тогда статус 200 и в теле не меньше двух элементов». body("title", is(...)) проверяет поле JSON-ответа, size() — длину массива. Объект NewPost RestAssured сам превращает в JSON.
Запуск
./mvnw testВывод сокращён:
INFO [io.quarkus.datasource.deployment.devservices.DevServicesDatasourceProcessor] Dev Services for default datasource (h2) started
INFO [io.quarkus] blog 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.40.1) started in 12.001s. Listening on: http://localhost:8081
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 -- in com.example.blog.PostResourceTest
[INFO] BUILD SUCCESS
Приложение стартует один раз на все тесты, на порту 8081, чтобы не мешать dev mode на 8080. Базу для тестов снова поднимает Dev Services, а Startup заполняет её двумя постами — поэтому проверка greaterThanOrEqualTo(2) проходит.
Непрерывное тестирование
В запущенном quarkus:dev нажмите r. Quarkus прогонит тесты и дальше будет перезапускать их после каждого сохранения файла — только те, которые затрагивает изменение. Результат виден внизу терминала и в разделе Continuous Testing в Dev UI.
Подмена REST-клиента
Тест не должен зависеть от чужого сервиса в интернете. Бин можно подменить моком — объектом-заглушкой. Добавьте зависимость:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit-mockito</artifactId>
<scope>test</scope>
</dependency>// src/test/java/com/example/blog/AuthorResourceTest.java
package com.example.blog;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
@QuarkusTest
class AuthorResourceTest {
@InjectMock
@RestClient
UserClient userClient;
@Test
void returnsAuthorFromUsersService() {
Mockito.when(userClient.getById(1))
.thenReturn(new User(1, "Анна", "anna@example.com"));
given()
.when().get("/authors/1")
.then()
.statusCode(200)
.body("name", is("Анна"));
}
}@InjectMock заменяет настоящий клиент на мок только в этом тесте, а Mockito.when(...).thenReturn(...) задаёт, что он вернёт. Запрос в интернет не уходит.
Частая ошибка
Если забыть .contentType(ContentType.JSON) в тесте createsPost, он упадёт:
[ERROR] PostResourceTest.createsPost:31 1 expectation failed.
Expected status code <201> but was <415>.
Это та же ошибка 415 Unsupported Media Type, что и с curl без заголовка: сервер не знает, что тело — JSON. Номер строки :31 указывает на проверку, которая не прошла.