Тестування є важливою частиною розробки програмного забезпечення, яка допомагає забезпечити якість та стабільність коду.
На наших курсах тестування ПЗ ми докладно розглядаємо тему автоматизації тестування. А в даній статті ми розглянемо популярні бібліотеки для Unit та Integration тестування в Java.
Бібліотека JUnit
JUnit – це основна бібліотека для юніт-тестування в Java. Вона допомагає забезпечити якість коду, перевіряючи окремі модулі програми на відповідність очікуваній поведінці. Ось декілька корисних можливостей JUnit:
- Анотації для структуризації тестів: JUnit використовує анотації, такі як
@Test
,@BeforeEach
,@AfterEach
,@BeforeAll
та@AfterAll
для визначення життєвого циклу тесту. - Перевірка результатів: JUnit містить клас
Assert
, який надає методи для перевірки результатів, такі якassertEquals
,assertTrue
,assertFalse
таassertNull
. - Параметризовані тести: JUnit дозволяє виконувати один і той же тест з різними наборами даних, що полегшує написання та підтримку тестів.
Припустимо, у вас є клас Calculator
з методом add, який приймає два цілих числа та повертає їх суму. Ось як можна написати юніт-тест JUnit для цього методу:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void testAdd() {
Calculator calculator = new Calculator();
int result = calculator.add(2, 3);
assertEquals(5, result);
}
}
Бібліотека Mockito
Mockito – це потужна бібліотека для створення та управління об’єктами-замінниками (mocks) в Java. Вона дозволяє імітувати залежності та перевіряти їх взаємодію з тестируваними модулями. Ось декілька корисних функцій Mockito:
- Створення макетів об’єктів: Mockito надає кілька способів створення макетів об’єктів, наприклад, за допомогою методу
mock()
або анотації@Mock
. - Перевірка поведінки макетів об’єктів: Mockito дозволяє перевіряти поведінку макетів об’єктів, такі як кількість викликів методів та їх аргументи.
- Заглушки (stubbing): Mockito дозволяє визначати поведінку макетів об’єктів за допомогою заглушок, які задають значення, що повертається методом при його виклику.
- Захоплення аргументів: Mockito дозволяє захоплювати аргументи, передані методу при виклику, що може бути корисним для перевірки поведінки компонентів.
Припустимо, у вас є клас DataService
, який залежить від іншого класу ExternalService
для отримання даних. Ви хочете протестувати, що DataService коректно обробляє дані, отримані від ExternalService
. Ось як можна використати Mockito для створення макету об’єкту ExternalService
та перевірки його взаємодії з DataService
:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
class DataServiceTest {
@Test
void testGetData() {
ExternalService mockExternalService = mock(ExternalService.class);
when(mockExternalService.fetchData()).thenReturn("mock data");
DataService dataService = new DataService(mockExternalService);
String result = dataService.getData();
assertEquals("mock data", result);
verify(mockExternalService, times(1)).fetchData();
}
}
Бібліотека TestNG
TestNG – це альтернативна бібліотека для юніт-тестування в Java, яка розширює можливості JUnit та надає додаткові функції, такі як:
- Конфігурація тестів: TestNG надає більше можливостей для налаштування тестів, включаючи конфігураційні файли та анотації.
- Залежності між тестами: TestNG дозволяє вказувати залежності між тестами, так що вони можуть бути виконані у визначеному порядку.
- Паралельне виконання тестів: TestNG підтримує паралельне виконання тестів, що може скоротити час виконання великих наборів тестів.
- Групування тестів: TestNG дозволяє групувати тести за різними критеріями, такими як функціональність, рівень складності або виключення з процесу тестування.
Припустимо, у вас є клас UserService
, який містить метод createUser
. Ви хочете написати юніт-тест для перевірки валідації введених даних користувача. Ось як можна використати TestNG для створення тесту:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
class UserServiceTest {
@DataProvider(name = "validUserData")
public Object[][] createValidUserData() {
return new Object[][] {
{"user1", "email1@example.com"},
{"user2", "email2@example.com"},
{"user3", "email3@example.com"}
};
}
@Test(dataProvider = "validUserData")
void testCreateUser(String username, String email) {
UserService userService = new UserService();
User result = userService.createUser(username, email);
assertEquals(username, result.getUsername());
assertEquals(email, result.getEmail());
}
}
Бібліотека AssertJ
AssertJ – це бібліотека, що дозволяє використовувати зручні та читабельні вирази для перевірки результатів тестів. Ця бібліотека працює з JUnit та TestNG та забезпечує ряд вдосконалень порівняно з класичними методами перевірки:
- Ланцюжкові вирази: AssertJ дозволяє використовувати ланцюжкові вирази для перевірки результатів, що робить код тестів більш читабельним та компактним.
- Кращі повідомлення про помилки: AssertJ автоматично генерує зрозумілі повідомлення про помилки, коли тестові перевірки не вдаються.
- Більший набір методів для перевірки: AssertJ містить багато додаткових методів для перевірки структур даних та типів, таких як колекції, масиви, дати та час.
Припустимо, у вас є клас ShoppingCart
, який містить метод getTotal
, що повертає загальну вартість товарів у кошику. Ось як можна використати AssertJ для перевірки результату методу:
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class ShoppingCartTest {
@Test
void testGetTotal() {
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.addItem(new Item("item1",10.0));
shoppingCart.addItem(new Item("item2", 20.0));
double result = shoppingCart.getTotal();
assertThat(result).isEqualTo(30.0);
}
}
Spring Test
Spring Test – це модуль тестування в рамках Spring Framework, який надає підтримку для юніт- та інтеграційного тестування Spring-додатків. Ось декілька особливостей Spring Test:
- Підтримка контексту Spring: Spring Test дозволяє автоматично завантажувати та налаштовувати контекст Spring в тестах, що дозволяє тестувати компоненти в ізольованому середовищі.
- Інтеграція з JUnit та TestNG: Spring Test підтримує інтеграцію з популярними бібліотеками юніт-тестування, такими як JUnit та TestNG.
- Інтеграційне тестування веб-додатків: Spring Test надає підтримку для тестування веб-контролерів та ендпоінтів за допомогою MockMvc, який дозволяє перевіряти поведінку веб-додатків без необхідності розгортати їх на сервері.
Припустимо, у вас є веб-контролер UserController
, який обробляє запити на створення користувачів. Ось як можна використати Spring Test для інтеграційного тестування методу створення користувача:
import org.junit.jupiter.api.Test;
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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testCreateUser() throws Exception {
mockMvc.perform(post("/users")
.param("username", "user1")
.param("email", "email1@example.com"))
.andExpect(status().isCreated());
}
}
Cucumber
Cucumber – це інструмент для тестування поведінки (Behavior-Driven Development, BDD), який дозволяє виконувати тести, написані у природній мові, та перетворювати їх на виконуваний код.
Cucumber співпрацює з JUnit та TestNG та має наступні особливості:
- Сценарії тестування у природній мові: Cucumber дозволяє описувати сценарії тестування у вигляді історій користувача, використовуючи специфікацію Gherkin, яка підтримує багато природних мов.
- Інтеграція з кодом: Cucumber використовує анотації для пов’язування кроків сценаріїв тестування з методами Java, що реалізують їх.
- Звіти та документація: Cucumber автоматично генерує звіти та документацію на основі сценаріїв тестування.
Припустимо, у вас є функціонал “реєстрація користувача” у вашому додатку. Ось як можна написати сценарій Cucumber для тестування цього функціоналу:
Feature: User registration
As a user
I want to register an account
So that I can access the application
Scenario: Successful registration
Given I am on the registration page
When I enter a valid username and email
And I submit the registration form
Then I should be registered and redirected to the home page
Для реалізації кроків сценарію, вам потрібно створити клас з методами, анотованими відповідними анотаціями Cucumber:
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
class UserRegistrationSteps {
@Given("I am on the registration page")
void openRegistrationPage() {
// ...
}
@When("I enter a valid username and email")
void enterValidUserData() {
// ...
}
@When("I submit the registration form")
void submitRegistrationForm() {
// ...
}
@Then("I should be registered and redirected to the home page")
void verifyRegistrationAndRedirection() {
// ...
}
}
Awaitility
Awaitility – це бібліотека для тестування асинхронного коду в Java, яка дозволяє легко перевіряти стан, що змінюється, за допомогою зручного API. Ось декілька особливостей Awaitility:
- Вирази чекання: Awaitility дозволяє використовувати вирази чекання для перевірки асинхронних умов, таких як завершення потоків, виконання операцій з I/O або зміни стану об’єктів.
- Конфігурування таймаутів: Awaitility дозволяє налаштовувати таймаути та інтервали опитування для перевірки умов чекання, що полегшує контроль над процесом тестування.
- Легка інтеграція з JUnit та TestNG: Awaitility може використовуватись разом з популярними бібліотеками юніт-тестування без будь-яких додаткових налаштувань.
Припустимо, у вас є асинхронний клас Downloader
, який завантажує файли з мережі. Ось як можна використати Awaitility для перевірки завершення завантаження:
import org.junit.jupiter.api.Test;
import static org.awaitility.Awaitility.await;
import static org.awaitility.Duration.*;
import static org.assertj.core.api.Assertions.assertThat;
class DownloaderTest {
@Test
void testDownloadCompletion() {
Downloader downloader = new Downloader();
downloader.downloadFile("https://example.com/file");
await().atMost(FIVE_SECONDS).until(() -> assertThat(downloader.getStatus()).isEqualTo(Downloader.Status.COMPLETED));
}
}
Wiser
Wiser – це легка бібліотека для тестування електронної пошти в Java-додатках, яка дозволяє перехоплювати електронні листи та перевіряти їх вміст, не надсилаючи їх на справжні поштові сервери.
Ось декілька особливостей Wiser:
- Вбудований SMTP-сервер: Wiser містить вбудований SMTP-сервер, який може використовуватись для перехоплення вихідних листів у тестовому середовищі.
- Перевірка вмісту листів: Wiser дозволяє перевіряти відправника, отримувачів, тему, текст та інші аспекти перехоплених листів.
- Легка інтеграція з JavaMail: Wiser може використовуватись з JavaMail API без будь-яких додаткових налаштувань.
Припустимо, у вас є клас EmailService
, який відправляє електронні листи користувачам. Ось як можна використати Wiser для тестування відправки електронної пошти:
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.subethamail.wiser.Wiser;
import org.subethamail.wiser.WiserMessage;
import javax.mail.internet.MimeMessage;
import static org.assertj.core.api.Assertions.assertThat;
class EmailServiceTest {
private Wiser wiser;
@BeforeEach
void setUp() {
wiser = new Wiser();
wiser.setPort(2500);
wiser.start();
}
@AfterEach
void tearDown() {
wiser.stop();
}
@Test
void testSendEmail() throws Exception {
EmailService emailService = new EmailService();
emailService.setSmtpHost("localhost");
emailService.setSmtpPort(2500);
emailService.sendEmail("user@example.com", "Test subject", "Test body");
List messages = wiser.getMessages();
assertThat(messages).hasSize(1);
MimeMessage message = messages.get(0).getMimeMessage();
assertThat(message.getSubject()).isEqualTo("Test subject");
assertThat(message.getContent()).isEqualTo("Test body");
}
}
Testcontainers
Testcontainers – це бібліотека для інтеграційного тестування Java-додатків, яка дозволяє використовувати Docker-контейнери для створення та керування зовнішніми залежностями, такими як бази даних, кеші, сервіси тощо.
Ось декілька особливостей Testcontainers:
- Управління Docker-контейнерами: Testcontainers дозволяє створювати, налаштовувати та контролювати контейнери Docker прямо з вашого коду тестування.
- Інтеграція з JUnit та TestNG: Testcontainers підтримує інтеграцію з популярними бібліотеками юніт-тестування, такими як JUnit та TestNG.
- Готові контейнери: Testcontainers містить набір готових контейнерів для популярних сервісів, таких як PostgreSQL, MySQL, Redis, Elasticsearch тощо.
Припустимо, у вас є клас UserRepository
, який працює з базою даних PostgreSQL. Ось як можна використати Testcontainers для інтеграційного тестування цього класу:
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import static org.assertj.core.api.Assertions.assertThat;
@Testcontainers
class UserRepositoryTest {
@Container
private static final PostgreSQLContainer> postgres = new PostgreSQLContainer<>("postgres:latest");
@Test
void testFindUserById() throws Exception {
try (Connection connection = DriverManager.getConnection(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword())) {
UserRepository userRepository = new UserRepository(connection);
userRepository.save(new User("John", "Doe"));
User user = userRepository.find(1);
assertThat(user).isNotNull();
assertThat(user.getFirstName()).isEqualTo("John");
assertThat(user.getLastName()).isEqualTo("Doe");
}
}
}
Memoryfilesystem
Memoryfilesystem – це бібліотека для створення віртуальних файлових систем у пам’яті для тестування Java-додатків, яка працює зі стандартним API файлів Java (java.nio.file). Ось декілька особливостей Memoryfilesystem:
- Швидка файлова система: Memoryfilesystem працює в оперативній пам’яті, що забезпечує високу швидкість доступу до файлів та директорій.
- Сумісність з java.nio.file: Memoryfilesystem використовує стандартні класи та інтерфейси Java для роботи з файлами, тому вам не потрібно вчити новий API.
- Легка інтеграція з тестами: Memoryfilesystem може використовуватись у будь-якому тестовому середовищі без додаткових налаштувань.
Припустимо, у вас є клас FileProcessor, який читає файли та обробляє їх вміст. Ось як можна використати Memoryfilesystem для тестування цього класу:
import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class FileProcessorTest {
private FileSystem fileSystem;
private FileProcessor fileProcessor;
@BeforeEach
void setUp() {
fileSystem = MemoryFileSystemBuilder.newEmpty().build();
fileProcessor = new FileProcessor();
}
@AfterEach
void tearDown() throws IOException {
fileSystem.close();
}
@Test
void testProcessFile() throws IOException {
Path inputFile = fileSystem.getPath("/input.txt");
Files.write(inputFile, "Hello, World!".getBytes());
Path outputFile = fileSystem.getPath("/output.txt");
fileProcessor.process(inputFile, outputFile);
assertThat(Files.exists(outputFile)).isTrue();
assertThat(Files.readAllLines(outputFile)).containsExactly("Processed: Hello, World!");
}
}
Висновки
У цій статті ми розглянули десять популярних бібліотек для юніт- та інтеграційного тестування Java-додатків. Кожна з них має свої переваги та особливості, які можуть допомогти вам поліпшити якість тестування та забезпечити надійність вашого коду. Завдяки цим бібліотекам ви зможете створювати більш ефективні тести та забезпечити успішну роботу вашого програмного забезпечення.
У процесі вибору бібліотеки для тестування вашого проекту враховуйте вимоги до функціональності, обсяг проекту та ваші персональні уподобання. Важливо пам’ятати, що якісне тестування – це одна з ключових складових успішного розробництва програмного забезпечення, і відповідний вибір інструментів для тестування може значно полегшити цей процес.