Java развивается, вводятся новые возможности, значительно улучшающие подходы в автоматизации QA. Позвольте мне рассказать об относительно свежих значимых дополнениях в Java, которые можно использовать в своих automation-проектах.
Шаблоны строк (Java 21 Preview, Java 24)
Шаблоны строк (String Templates) в Java 21 и 24 представляют собой значительное улучшение операций с динамическими строками.
В Java традиционно используется конкатенация строк — или String.format()
для создания строк с изменяемым содержимым. У обоих подходов есть недостатки.
- Конкатенация становится беспорядочной при большом количестве переменных.
String.format()
требует сопоставления спецификаторов формата (%s, %d
) с переменными в правильном порядке.- Оба способа чреваты ошибками и трудночитаемы в сложных строках.
Шаблоны строк позволяют вставлять выражения непосредственно в строки в понятном и читаемом виде.
// Использование функции String Template // Префикс STR. перед строкой (это процессор шаблонов) // Использование фигурных скобок \{выражение} для вставки значений // Возможность поместить любое допустимое выражение Java внутрь скобок String name = "Test"; int age = 25; String message = STR."Hello, my name is \{name} and I am \{age} years old.";
@Test void testUserRegistration() { // Тестовые данные String username = "testUser123"; String email = "test@example.com"; String password = "SecureP@ss"; // Использование шаблона строки для создания JSON-данных String requestBody = STR.""" { "username": "\{username}", "email": "\{email}", "password": "\{password}", "createDate": "\{LocalDate.now()}" } """; // Используйте это тело запроса в своем тесте Response response = apiClient.post("/users", requestBody); // Ассерты assertEquals(201, response.getStatusCode()); }
Согласование шаблонов (Pattern Matching) (Java 16+)
Согласование шаблонов для instanceof изменило практику проверки типов и их приведение (casting) в тестовом коде. Эта функция сокращает количество шаблонов в тестовых ассертах и делает валидацию условий более читабельной.
// Старый подход if (testResult instanceof SuccessResult) { SuccessResult success = (SuccessResult) testResult; verifySuccessData(success.getData()); } // Новый подход с согласованием if (testResult instanceof SuccessResult success) { verifySuccessData(success.getData()); }
Записи (Records) (Java 16+)
Records отлично подходят для создания простых, неизменяемых носителей данных (data carriers) в тестовом коде. Записи избавляют от необходимости писать конструкторы, геттеры, методы equals()
, hashCode()
и toString()
для объектов тестовых данных.
// // Лаконично определяем структуры тестовых данных record TestUser(String username, String email, boolean isActive) {} record ExpectedResponse(int statusCode, String body, Map<String, String> headers) {} @Test void verifyUserRegistration() { TestUser user = new TestUser("testUser", "test@example.com", true); ExpectedResponse expected = new ExpectedResponse(200, "Success", Map.of("Content-Type", "application/json")); // Используем эти неизменяемые объекты в тестах ResponseEntity response = userService.register(user); assertEquals(expected.statusCode(), response.getStatusCode().value()); }
Текстовые блоки (Java 15+)
Текстовые блоки (Text Blocks) упрощают работу с многострочными строками, что полезно для тестовых данных в JSON/XML и SQL-запросах.
String jsonTestData = """ { "username": "testUser", "password": "securePass123", "roles": ["user", "admin"], "settings": { "notifications": true, "theme": "dark" } } """;
Stream.toList() (Java 16+)
Небольшое, но значительное улучшение, упрощающее сбор результатов потока.
// Старый подход List<String> errorMessages = testResults.stream() .filter(result -> !result.isPassed()) .map(TestResult::getMessage) .collect(Collectors.toList()); // Новый подход List<String> errorMessages = testResults.stream() .filter(result -> !result.isPassed()) .map(TestResult::getMessage) .toList();
Stream.mapMulti() (Java 16+)
Пригодится для упрощения сложных тестовых структур данных.
List<TestScenario> flattenedTestCases = testSuites.stream() .mapMulti((suite, consumer) -> { suite.getTestCases().forEach(consumer); }) .toList();
Исключения NullPointerExceptions (Java 14+)
Эта функция предоставляет точную информацию о том, какая именно переменная была null в цепочке.
// При неудаче с NPE в сообщении об исключении будет указано, какая именно часть была null. user.getAddress().getCity().equals("New York"); // Вы можете сразу увидеть, что getCity() вернула null, что указывает непосредственно на источник проблемы. java.lang.NullPointerException: Cannot invoke "com.example.City.equals(Object)" because the return value of "com.example.Address.getCity()" is null at com.example.YourClass.yourMethod(YourClass.java:42)
Улучшения Switch-выражений (Java 14+)
Выражения-переключатели (Switch expressions) делают логику тестов более лаконичной.
TestResult evaluateResponse(HttpStatus status) { return switch(status) { case OK, CREATED, ACCEPTED -> new TestResult(true, "Success"); case NOT_FOUND -> new TestResult(false, "Resource not found"); case UNAUTHORIZED, FORBIDDEN -> new TestResult(false, "Authentication error"); default -> new TestResult(false, "Unexpected status: " + status); }; }
Улучшения класса String
В класс String было добавлено несколько полезных методов.
// isBlank() - Java 11 assertTrue(response.getBody().isBlank()); // методы strip() - Java 11 assertEquals("expected", response.getHeader("X-Value").strip()); // transform() - Java 12 List<String> logLines = testOutput.lines().toList(); // transform() - Java 12 String normalizedOutput = rawTestOutput.transform(String::toLowerCase) .transform(s -> s.replaceAll("\\s+", " ")); // formatted() - Java 15 (замена для String.format) String testMessage = "Expected %s but got %s".formatted(expected, actual); // indent() - Java 12 String prettyJson = rawJson.indent(2);
Улучшенная генерация случайных чисел (Java 17+)
Генераторы псевдослучайных чисел были усовершенствованы, что полезно для генерации случайных тестовых данных.
// Создаем новый генератор случайных чисел RandomGenerator generator = RandomGenerator.of("L64X128MixRandom"); // Генерируем случайные тестовые данные int randomAge = generator.nextInt(18, 99); double randomSalary = generator.nextDouble(30000, 150000);
Убедитесь, что ваш проект настроен на использование новой версии Java (для большинства описанных выше функций рекомендуется Java 17+).