← Назад к вопросам

В чем разница между unit-тестами и integration тестами и end-to-end тестами?

1.0 Junior🔥 61 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Различие между 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, MockitoSpringBootTestSelenium, REST Assured

Пирамида тестирования

      /\
     /E2E\
    /----\
   /Integration\
  /----------\
 /  Unit Tests\

Стратегия: Много unit-тестов, средне integration-тестов, мало E2E тестов. Это обеспечивает быструю обратную связь на этапе разработки и уверенность в готовности к production.

Лучшие практики

  1. Unit-тесты: Должны быть быстрыми и независимыми. Используй mocks для внешних зависимостей.
  2. Integration-тесты: Тестируй реальные взаимодействия, используй тестовую БД.
  3. E2E-тесты: Покрывай только критические пути пользователя.
  4. CI/CD: Unit-тесты в каждом коммите, Integration — в PR, E2E — перед production.

Вывод: Комбинация всех трех уровней дает уверенность в качестве кода и функциональности приложения.