«Код без тестов — это legacy»: почему вам стоит начать уже сегодня
Представьте: вы написали приложение на Java. Оно работает. Но вы боитесь что-то менять, потому что одно неверное движение — и всё рухнет. Знакомо? Добро пожаловать в мир, где тестирование Java — не прихоть, а навык выживания.
Многие начинающие разработчики откладывают тесты «на потом». Мол, сначала научусь писать код, а потом разберусь. Но правда в том, что юнит-тесты Java — это не дополнительная нагрузка, а ваш страховочный трос. Они заставляют вас писать более качественный, модульный и понятный код. И да, работодатели очень ценят разработчиков, которые умеют покрывать код тестами.
В этой статье мы разберем, с чего начать тестирование Java новичку. Вы узнаете про основные инструменты — JUnit и Mockito, напишете свои первые тесты и поймете, как не скатиться в «тестирование ради тестирования». Поехали!
Шаг 1: Настройка окружения — ваш первый тестовый стенд
Прежде чем писать тесты, нужно подготовить среду. Хорошая новость: для автоматизации тестирования Java вам не нужно ничего сверхъестественного. Всё, что у вас уже есть, — это JDK и IDE (IntelliJ IDEA или Eclipse).
Maven или Gradle — выбираем сборщик
Современные Java-проекты редко обходятся без системы сборки. Мы будем использовать Maven — он де-факто стандарт для большинства проектов. Вот минимальный pom.xml с зависимостями для тестирования:
<dependencies>
<!-- JUnit 5 — основной фреймворк для тестов -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- Mockito для создания mock-объектов -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Обратите внимание на <scope>test</scope> — эти библиотеки не попадут в финальную сборку приложения, они нужны только для запуска тестов.
Шаг 2: JUnit — ваш первый настоящий тест
JUnit — это фундамент, на котором строится всё тестирование Java. С его помощью вы пишете юнит-тесты: проверяете работу одного метода или класса в изоляции.
Пишем первый тест
Допустим, у нас есть простой калькулятор:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Делить на ноль нельзя!");
}
return a / b;
}
}
Теперь напишем тест для него. Создайте класс в папке src/test/java:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
private final Calculator calculator = new Calculator();
@Test
void testAdd() {
// Arrange (подготовка)
int a = 2;
int b = 3;
// Act (действие)
int result = calculator.add(a, b);
// Assert (проверка)
assertEquals(5, result, "2 + 3 должно быть равно 5");
}
@Test
void testDivideByZero() {
// Проверяем, что метод выбрасывает исключение
assertThrows(IllegalArgumentException.class,
() -> calculator.divide(10, 0));
}
}
Запустите тест — зеленая полоска в IDE скажет вам, что всё работает. Поздравляю, вы только что сделали первый шаг в unit testing Java!
Паттерн AAA: Arrange-Act-Assert
Обратите внимание на структуру теста. Это не случайность. Лучшие практики тестирования Java рекомендуют придерживаться паттерна AAA:
- Arrange — подготовка данных и объектов
- Act — выполнение тестируемого действия
- Assert — проверка результата
Это делает тесты читаемыми и понятными даже для тех, кто видит их впервые.
Шаг 3: Mockito — когда тестировать нечего, но очень хочется
В реальных проектах классы редко существуют сами по себе. Они общаются с базой данных, вызывают внешние API, работают с файловой системой. Как протестировать такой код, не поднимая настоящую базу?
На помощь приходит Mockito — библиотека для создания mock-объектов (заглушек).
Пример: тестируем сервис с зависимостью
Представьте сервис, который сохраняет пользователя и отправляет ему email:
public class UserService {
private final UserRepository repository;
private final EmailService emailService;
public UserService(UserRepository repository, EmailService emailService) {
this.repository = repository;
this.emailService = emailService;
}
public User registerUser(String name, String email) {
User user = new User(name, email);
repository.save(user); // сохраняем в БД
emailService.sendWelcomeEmail(user); // отправляем письмо
return user;
}
}
Чтобы протестировать этот метод, нам нужно проверить, что:
repository.save()был вызван с правильным пользователемemailService.sendWelcomeEmail()был вызван один раз
Вот как это делается с Mockito:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) // Включаем поддержку Mockito в JUnit 5
class UserServiceTest {
@Mock
private UserRepository repository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
void testRegisterUser() {
// Arrange
String name = "Иван";
String email = "ivan@example.com";
// Act
User result = userService.registerUser(name, email);
// Assert
assertEquals(name, result.getName());
assertEquals(email, result.getEmail());
// Проверяем, что методы были вызваны
verify(repository).save(any(User.class));
verify(emailService, times(1)).sendWelcomeEmail(any(User.class));
}
}
Обратите внимание на аннотации: @Mock создает заглушку, а @InjectMocks автоматически внедряет эти заглушки в тестируемый объект. Mock-объекты Java позволяют изолировать тестируемый