QA Interview Hub · База знань · Тести

Java: основи

Перевірки й API-тести

AssertJ: читабельні перевірки та м’які твердження

AssertJ — бібліотека плавних (fluent) перевірок. Усе починається з assertThat(actual), далі ланцюжок методів, які підказує IDE:

import static org.assertj.core.api.Assertions.assertThat;

assertThat(user.name()).isEqualTo("Anna");
assertThat(names).hasSize(3).contains("Anna").doesNotHaveDuplicates();
assertThat(price).isBetween(10, 100);
assertThat(order).usingRecursiveComparison().isEqualTo(expectedOrder);

Перевагою над простим assertEquals є зрозуміле повідомлення про помилку («Expecting … to contain …») і порядок «фактичне, потім очікуване» без плутанини з аргументами. usingRecursiveComparison() порівнює об’єкти поле за полем без перевизначеного equals, extracting витягує поля зі списку об’єктів. Для винятків — assertThatThrownBy(() -> ...).isInstanceOf(X.class).hasMessageContaining("...").

М’які твердження (soft assertions) не зупиняються на першій помилці, а збирають усі й показують разом. Корисно, коли перевіряється кілька незалежних полів і хочеться побачити всю картину за один прогін:

SoftAssertions.assertSoftly(softly -> {
    softly.assertThat(user.name()).isEqualTo("Anna");
    softly.assertThat(user.role()).isEqualTo("admin");
    softly.assertThat(user.active()).isTrue();
});

У assertSoftly перевірка запускається автоматично наприкінці блоку. Якщо створити new SoftAssertions() вручну, треба викликати softly.assertAll(): без нього збої ігноруються й тест «зеленіє». У JUnit 5 можна підключити SoftAssertionsExtension із полем @InjectSoftAssertions.

Коли не варто: коли наступна перевірка залежить від попередньої (немає сенсу перевіряти поле, якщо об’єкт null): там потрібна звичайна, жорстка.

JUnit 5 і TestNG мають власні Assertions, а Hamcrest — assertThat(actual, is(expected)) (його матчери використовує REST Assured). Для нового коду AssertJ зручніший.

REST Assured, Builder для даних і Lombok

REST Assured — DSL для API-тестів у стилі BDD. Ланцюжок given / when / then:

  • given() — умови: базовий URL, заголовки, параметри, тіло, авторизація;
  • when() — дія: get("/users/1"), post("/users");
  • then() — перевірки: statusCode(201), header(...), body("id", notNullValue()) (матчери Hamcrest, шляхи у стилі JsonPath/GPath).
User created = given()
    .spec(requestSpec)
    .body(newUser)                      // POJO серіалізується в JSON
.when()
    .post("/users")
.then()
    .statusCode(201)
    .extract().as(User.class);          // десеріалізація в POJO

assertThat(created.name()).isEqualTo(newUser.name());

Для серіалізації потрібна бібліотека на classpath (Jackson, Gson чи JSON-B). Спільні налаштування виносять у RequestSpecification (через RequestSpecBuilder): базовий URI, content type, авторизація, log().ifValidationFails(), щоб бачити запит і відповідь лише в разі збою. Окремий extract().path("id") бере одне значення. Перевірка схеми — модуль json-schema-validator.

Builder для тестових даних. Конструктор із десятьма параметрами нечитабельний. Builder задає валідні значення за замовчуванням, а тест змінює лише поле, важливе для сценарію:

User admin = UserBuilder.aUser().withRole(Role.ADMIN).build();
User noEmail = UserBuilder.aUser().withEmail(null).build();  // негативний кейс

Унікальні значення (email із суфіксом, випадковий логін) генеруйте у builder, щоб паралельні тести не конфліктували.

Lombok генерує код під час компіляції через annotation processing: @Getter/@Setter, @Builder, @Value (незмінний клас), @Slf4j (поле логера). Він додається як залежність provided й потребує плагіна в IDE; у Maven його процесор варто явно прописати в annotationProcessorPaths компілятора (з JDK 23 обробники з classpath за замовчуванням не запускаються). Обережно з @Data на змінюваних класах: він генерує equals/hashCode за всіма полями. Для простих даних сучасна альтернатива — record.