В чем разница между unit-тестами и integration тестами и end-to-end тестами?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Различие между Unit, Integration и E2E тестами
Тестирование является критической частью разработки Java приложений. Существует три основных уровня тестирования, которые различаются по масштабу, скорости и целям.
Unit-тесты
Unit-тесты проверяют отдельные компоненты кода в изоляции — обычно отдельные методы или классы.
public class CalculatorTest {
private Calculator calculator;
@Before
public void setUp() {
calculator = new Calculator();
}
@Test
public void testAdd() {
int result = calculator.add(2, 3);
assertEquals(5, result);
}
@Test
public void testAddNegativeNumbers() {
int result = calculator.add(-2, -3);
assertEquals(-5, result);
}
}
Характеристики unit-тестов:
- Масштаб: Тестируют одну функцию/метод
- Скорость: Очень быстрые (миллисекунды)
- Зависимости: Используют mocks и stubs (Spring @MockBean, Mockito)
- Окружение: Не требуют БД, внешних сервисов
- Количество: Много тестов (большое покрытие)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
public void testFindUserById() {
User expectedUser = new User(1L, "John");
when(userRepository.findById(1L)).thenReturn(Optional.of(expectedUser));
User result = userService.findUserById(1L);
assertEquals("John", result.getName());
verify(userRepository).findById(1L);
}
}
Integration-тесты
Integration-тесты проверяют взаимодействие нескольких компонентов системы вместе — контроллеры с сервисами, сервисы с БД и т.д.
@SpringBootTest
@ActiveProfiles("test")
public class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
@Transactional
public void testCreateAndFindUser() {
// Создаем пользователя через сервис
User user = new User("Alice", "alice@example.com");
User savedUser = userService.createUser(user);
// Проверяем, что он действительно сохранился в БД
User foundUser = userRepository.findById(savedUser.getId()).get();
assertEquals("Alice", foundUser.getName());
}
@Test
public void testUserRepositoryIntegration() {
userRepository.save(new User("Bob", "bob@example.com"));
List<User> users = userRepository.findAll();
assertTrue(users.stream().anyMatch(u -> "Bob".equals(u.getName())));
}
}
Характеристики integration-тестов:
- Масштаб: Несколько взаимодействующих компонентов
- Скорость: Медленнее unit-тестов (несколько секунд)
- Зависимости: Реальная БД (обычно в памяти H2), реальные сервисы
- Окружение: Требуют контекста Spring, БД для тестирования
- Количество: Меньше, чем unit-тестов
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private UserRepository userRepository;
@Test
public void testCreateUserEndpoint() {
User newUser = new User("Charlie", "charlie@example.com");
ResponseEntity<User> response = restTemplate.postForEntity(
"/api/users", newUser, User.class);
assertEquals(HttpStatus.CREATED, response.getStatusCode());
assertTrue(userRepository.findAll().stream()
.anyMatch(u -> "Charlie".equals(u.getName())));
}
}
End-to-End (E2E) тесты
E2E тесты проверяют полный сценарий использования приложения от начала до конца, имитируя реальные действия пользователя.
// С использованием Selenium для веб-приложений
public class UserRegistrationE2ETest {
private WebDriver driver;
@Before
public void setUp() {
driver = new ChromeDriver();
driver.get("http://localhost:8080/register");
}
@Test
public void testCompleteRegistrationFlow() {
// Пользователь видит форму регистрации
WebElement nameInput = driver.findElement(By.id("name"));
WebElement emailInput = driver.findElement(By.id("email"));
WebElement passwordInput = driver.findElement(By.id("password"));
WebElement submitButton = driver.findElement(By.id("submit"));
// Заполняет форму
nameInput.sendKeys("David");
emailInput.sendKeys("david@example.com");
passwordInput.sendKeys("password123");
// Отправляет форму
submitButton.click();
// Проверяет редирект на страницу профиля
wait.until(ExpectedConditions.urlContains("/profile"));
assertEquals("http://localhost:8080/profile", driver.getCurrentUrl());
}
@After
public void tearDown() {
driver.quit();
}
}
Для современных веб-приложений часто используют REST Assured или TestNG:
@Test
public void testCompleteOrderFlow() {
// Шаг 1: Пользователь логинится
String token = given()
.contentType(ContentType.JSON)
.body(new LoginRequest("user@example.com", "password"))
.when()
.post("/api/v1/auth/login")
.then()
.statusCode(200)
.extract()
.path("token");
// Шаг 2: Создает заказ
int orderId = given()
.header("Authorization", "Bearer " + token)
.contentType(ContentType.JSON)
.body(new OrderRequest("Item1", 2))
.when()
.post("/api/v1/orders")
.then()
.statusCode(201)
.extract()
.path("id");
// Шаг 3: Проверяет статус заказа
given()
.header("Authorization", "Bearer " + token)
.when()
.get("/api/v1/orders/" + orderId)
.then()
.statusCode(200)
.body("status", equalTo("PENDING"));
}
Характеристики E2E тестов:
- Масштаб: Полный пользовательский сценарий от начала до конца
- Скорость: Самые медленные (минуты)
- Зависимости: Реальное окружение, БД, браузер или API клиент
- Окружение: Работают с развернутым приложением
- Количество: Мало, только критичные сценарии
Сравнительная таблица
| Аспект | Unit-тесты | Integration-тесты | E2E-тесты |
|---|---|---|---|
| Масштаб | Один метод | Несколько компонентов | Весь сценарий |
| Скорость | Очень быстро | Медленно | Очень медленно |
| Зависимости | Mocks/Stubs | Реальные (H2 БД) | Полное окружение |
| Покрытие | 70-90% | 50-70% | 10-20% |
| Стоимость | Дешево | Дорого | Очень дорого |
| Возвращаемость | Высокая | Средняя | Низкая |
| Примеры | JUnit, Mockito | SpringBootTest | Selenium, REST Assured |
Пирамида тестирования
/\
/E2E\
/----\
/Integration\
/----------\
/ Unit Tests\
Стратегия: Много unit-тестов, средне integration-тестов, мало E2E тестов. Это обеспечивает быструю обратную связь на этапе разработки и уверенность в готовности к production.
Лучшие практики
- Unit-тесты: Должны быть быстрыми и независимыми. Используй mocks для внешних зависимостей.
- Integration-тесты: Тестируй реальные взаимодействия, используй тестовую БД.
- E2E-тесты: Покрывай только критические пути пользователя.
- CI/CD: Unit-тесты в каждом коммите, Integration — в PR, E2E — перед production.
Вывод: Комбинация всех трех уровней дает уверенность в качестве кода и функциональности приложения.