Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Пирамида тестирования
Пирамида тестирования — это концепция, которая описывает оптимальное распределение различных типов тестов в проекте. Идея заключается в том, что нужно иметь много быстрых unit-тестов, меньше интеграционных тестов и совсем немного медленных E2E тестов. Это обеспечивает баланс между полнотой покрытия и скоростью обратной связи при разработке.
Структура пирамиды (снизу вверх)
1. Unit-тесты (база пирамиды)
- Количество: максимальное (50-60% всех тестов)
- Скорость: очень быстрые (миллисекунды)
- Масштаб: тестируют отдельные методы и классы
- Изоляция: полная изоляция от внешних зависимостей (БД, API)
- Инструменты: JUnit, Mockito, Testng
Unit-тесты проверяют логику отдельных компонентов в изоляции:
// Тест для бизнес-логики
public class CalculatorTest {
@Test
public void testAddition() {
Calculator calc = new Calculator();
int result = calc.add(2, 3);
assertEquals(5, result);
}
@Test
public void testDivisionByZero() {
Calculator calc = new Calculator();
assertThrows(IllegalArgumentException.class, () -> calc.divide(10, 0));
}
}
2. Интеграционные тесты (середина пирамиды)
- Количество: среднее (20-30% всех тестов)
- Скорость: медленнее, чем unit-тесты (секунды)
- Масштаб: тестируют взаимодействие нескольких компонентов
- Изоляция: могут использовать реальную БД, API, сообщения очередей
- Инструменты: TestContainers, @DataJpaTest, @WebMvcTest
Интеграционные тесты проверяют, как компоненты работают вместе:
// Интеграционный тест с БД
@DataJpaTest
public class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
public void testFindUserByEmail() {
User user = new User("test@example.com", "John");
userRepository.save(user);
User found = userRepository.findByEmail("test@example.com");
assertNotNull(found);
assertEquals("John", found.getName());
}
}
3. E2E тесты (вершина пирамиды)
- Количество: минимальное (10-15% всех тестов)
- Скорость: медленные (десятки секунд или минуты)
- Масштаб: тестируют полный пользовательский сценарий
- Изоляция: нет изоляции, используется полная система
- Инструменты: Selenium, Playwright, Cypress, TestNG
E2E тесты проверяют, что приложение работает корректно с точки зрения пользователя:
// E2E тест UI с Selenium
public class LoginE2ETest {
private WebDriver driver;
@Before
public void setUp() {
driver = new ChromeDriver();
driver.get("http://localhost:8080/login");
}
@Test
public void testSuccessfulLogin() {
WebElement emailInput = driver.findElement(By.id("email"));
emailInput.sendKeys("user@example.com");
WebElement passwordInput = driver.findElement(By.id("password"));
passwordInput.sendKeys("password123");
WebElement loginButton = driver.findElement(By.id("loginBtn"));
loginButton.click();
WebElement dashboard = driver.findElement(By.id("dashboard"));
assertTrue(dashboard.isDisplayed());
}
@After
public void tearDown() {
driver.quit();
}
}
Практическая реализация
// Service с бизнес-логикой
public class PaymentService {
private PaymentRepository paymentRepository;
private NotificationService notificationService;
public PaymentService(PaymentRepository repo, NotificationService notify) {
this.paymentRepository = repo;
this.notificationService = notify;
}
public Payment processPayment(long orderId, BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
Payment payment = new Payment(orderId, amount);
paymentRepository.save(payment);
notificationService.notifySuccess(orderId);
return payment;
}
}
// Unit-тест (мокируем зависимости)
@RunWith(MockitoJUnitRunner.class)
public class PaymentServiceUnitTest {
@Mock
private PaymentRepository paymentRepository;
@Mock
private NotificationService notificationService;
@InjectMocks
private PaymentService paymentService;
@Test
public void testProcessPaymentSuccess() {
BigDecimal amount = new BigDecimal("100.00");
Payment expectedPayment = new Payment(1L, amount);
when(paymentRepository.save(any())).thenReturn(expectedPayment);
Payment result = paymentService.processPayment(1L, amount);
assertNotNull(result);
assertEquals(amount, result.getAmount());
verify(notificationService).notifySuccess(1L);
}
@Test
public void testProcessPaymentInvalidAmount() {
assertThrows(IllegalArgumentException.class,
() -> paymentService.processPayment(1L, BigDecimal.ZERO));
}
}
Рекомендуемое распределение
- Unit-тесты: 70-75%
- Интеграционные тесты: 15-20%
- E2E тесты: 5-10%
Это распределение обеспечивает быстрый feedback при разработке (unit-тесты запускаются за секунды) и уверенность в правильности работы системы (E2E тесты проверяют реальные сценарии).
Преимущества пирамиды тестирования
- Скорость обратной связи: unit-тесты дают результат почти мгновенно
- Стабильность: fewer flaky tests благодаря минимизации E2E тестов
- Экономия ресурсов: unit-тесты требуют мало вычислительных ресурсов
- Локализация ошибок: unit-тесты помогают быстро найти причину проблемы
- Уверенность в коде: E2E тесты подтверждают, что система работает в целом