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

В чем разница между unit тестами и функциональными тестами?

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

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

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

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

В чем разница между unit тестами и функциональными тестами?

Unit тесты и функциональные тесты проверяют разные уровни приложения. Unit тесты проверяют маленькие части кода изолированно, функциональные тесты проверяют целые сценарии использования конца-в-конец. Обе необходимы для полного покрытия качества.

Unit тесты: тестирование компонентов

Unit тест проверяет один метод/класс изолированно

// Класс для тестирования
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public int multiply(int a, int b) {
        return a * b;
    }
}

// Unit тесты
@ExtendWith(MockitoExtension.class)
class CalculatorTest {
    private Calculator calculator;
    
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }
    
    @Test
    void testAdd() {
        // Arrange
        int a = 2, b = 3;
        
        // Act
        int result = calculator.add(a, b);
        
        // Assert
        assertEquals(5, result);
    }
    
    @Test
    void testMultiply() {
        int result = calculator.multiply(3, 4);
        assertEquals(12, result);
    }
    
    @Test
    void testAddWithNegatives() {
        int result = calculator.add(-2, -3);
        assertEquals(-5, result);
    }
}

Unit тест с mocks

// Класс с зависимостями
public class UserService {
    private UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
    
    public User findById(int id) {
        return repository.findById(id);
    }
}

// Unit тест (изолированный)
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository mockRepository;
    
    private UserService service;
    
    @BeforeEach
    void setUp() {
        service = new UserService(mockRepository);
    }
    
    @Test
    void testFindByIdReturnUser() {
        // Arrange
        User expectedUser = new User(1, "John");
        when(mockRepository.findById(1)).thenReturn(expectedUser);
        
        // Act
        User result = service.findById(1);
        
        // Assert
        assertEquals("John", result.getName());
        verify(mockRepository).findById(1);
    }
    
    @Test
    void testFindByIdNotFound() {
        // Arrange
        when(mockRepository.findById(999)).thenReturn(null);
        
        // Act
        User result = service.findById(999);
        
        // Assert
        assertNull(result);
    }
}

Функциональные тесты: тестирование поведения

Функциональный тест проверяет полный сценарий использования

// Интеграционный функциональный тест
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerFunctionalTest {
    @Autowired
    private MockMvc mockMvc;
    
    @Autowired
    private UserRepository repository;
    
    @Test
    void testGetUserFlow() throws Exception {
        // Arrange: создаем пользователя в БД
        User user = new User(1, "John", "john@example.com");
        repository.save(user);
        
        // Act & Assert: проверяем весь flow
        mockMvc.perform(get("/users/1")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("John"))
            .andExpect(jsonPath("$.email").value("john@example.com"));
    }
    
    @Test
    void testCreateUserFlow() throws Exception {
        // Arrange
        String newUserJson = "{\"name\":\"Jane\",\"email\":\"jane@example.com\"}";
        
        // Act & Assert: полный flow создания
        mockMvc.perform(post("/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content(newUserJson))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").exists())
            .andExpect(jsonPath("$.name").value("Jane"));
        
        // Verify: проверяем что сохранилось в БД
        User saved = repository.findByEmail("jane@example.com");
        assertNotNull(saved);
    }
    
    @Test
    void testUserWorkflow() throws Exception {
        // 1. Создание пользователя
        String createJson = "{\"name\":\"Bob\",\"email\":\"bob@example.com\"}";
        MvcResult createResult = mockMvc.perform(post("/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content(createJson))
            .andExpect(status().isCreated())
            .andReturn();
        
        // 2. Получение созданного пользователя
        mockMvc.perform(get("/users/bob@example.com")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Bob"));
        
        // 3. Обновление пользователя
        String updateJson = "{\"name\":\"Robert\"}";
        mockMvc.perform(put("/users/bob@example.com")
            .contentType(MediaType.APPLICATION_JSON)
            .content(updateJson))
            .andExpect(status().isOk());
        
        // 4. Проверка обновления
        mockMvc.perform(get("/users/bob@example.com"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Robert"));
    }
}

E2E (End-to-End) тест

// E2E тест с реальным браузером
class UserApplicationE2ETest {
    private WebDriver driver;
    
    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
        driver.navigate().to("http://localhost:8080");
    }
    
    @Test
    void testCompleteUserRegistrationFlow() {
        // 1. Переходим на страницу регистрации
        driver.findElement(By.linkText("Register")).click();
        
        // 2. Заполняем форму
        driver.findElement(By.id("name")).sendKeys("John Doe");
        driver.findElement(By.id("email")).sendKeys("john@example.com");
        driver.findElement(By.id("password")).sendKeys("password123");
        
        // 3. Отправляем форму
        driver.findElement(By.id("submit")).click();
        
        // 4. Проверяем успешность
        String message = driver.findElement(By.className("success")).getText();
        assertEquals("Registration successful", message);
        
        // 5. Проверяем редирект на профиль
        assertEquals("http://localhost:8080/profile", driver.getCurrentUrl());
    }
    
    @AfterEach
    void tearDown() {
        driver.quit();
    }
}

Сравнение

АспектUnit тестыФункциональные тесты
ОбластьОдин метод/классПолный сценарий
СкоростьБыстро (ms)Медленно (сек/мин)
MocksМного (изолируем)Минимум (реальное)
ПростотаПростыеСложные
Кол-воМного (сотни)Мало (десятки)
ПоддержкаЛегко рефакторитьХрупкие (brittle)
ПокрытиеДетальноеВысокоуровневое
ПримерыJUnit, MockitoIntegration, Selenium

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

               /\
              /  \
             / E2E \ (10%)
            /________\
           /          \
          /Functional  \ (30%)
         /____________\
        /              \
       /  Unit Tests    \ (60%)
      /________________\

Unit тесты

Плюсы:

  • ✅ Быстрые (ms)
  • ✅ Легко писать
  • ✅ Высокое покрытие
  • ✅ Легко найти проблему

Минусы:

  • ❌ Не ловят интеграционные баги
  • ❌ Mocks могут скрывать проблемы
  • ❌ Не проверяют реальное поведение

Функциональные тесты

Плюсы:

  • ✅ Проверяют реальное поведение
  • ✅ Ловят интеграционные баги
  • ✅ Проверяют API contract

Минусы:

  • ❌ Медленные
  • ❌ Сложные для написания
  • ❌ Хрупкие (brittle)
  • ❌ Сложно найти причину проблемы

Пример комплексного подхода

// 1. Unit тесты: бизнес логика
@Test
void testCalculateDiscount() {
    // Проверяем только логику расчета
    Order order = new Order(100);
    int discount = order.calculateDiscount(10);  // 10% discount
    assertEquals(10, discount);
}

// 2. Интеграционные тесты: слои работают вместе
@SpringBootTest
class OrderServiceIntegrationTest {
    @Test
    void testOrderSaveAndRetrieve() {
        Order order = new Order(100);
        service.save(order);  // Реальная БД
        
        Order retrieved = service.findById(order.getId());
        assertEquals(100, retrieved.getAmount());
    }
}

// 3. Функциональные тесты: весь flow
@Test
void testOrderCreationFlow() {
    // 1. Создание заказа
    mockMvc.perform(post("/orders")
        .content("{\"amount\":100}"))
        .andExpect(status().isCreated());
    
    // 2. Получение заказа
    mockMvc.perform(get("/orders/1"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.amount").value(100));
}

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

// ✅ Хорошо: разделение на уровни
public class OrderTest {
    // Unit: только бизнес логика
    @Test
    void testApplyDiscount() {
        Order order = new Order(100);
        order.applyDiscount(10);
        assertEquals(90, order.getTotal());
    }
}

@SpringBootTest
class OrderControllerTest {
    // Функциональный: API level
    @Test
    void testCreateOrderEndpoint() {
        mockMvc.perform(post("/api/orders")
            .content("{\"amount\":100}"))
            .andExpect(status().isCreated());
    }
}

// ❌ Плохо: смешивание уровней
@Test
void testEverything() {
    Order order = new Order(100);
    service.save(order);
    repository.findById(order.getId());
    String json = objectMapper.writeValueAsString(order);
    // Что мы здесь тестируем?
}

Инструменты

Unit тестирование:

  • JUnit (основной фреймворк)
  • Mockito (mocking)
  • AssertJ (assertions)
  • Parameterized tests

Функциональное тестирование:

  • Spring MockMvc (API тесты)
  • RestAssured (REST API)
  • Selenium (UI)
  • Testcontainers (интеграция)

Вывод

Unit тесты:

  • Пишите МНОГО
  • Проверяйте бизнес логику
  • Используйте mocks для изоляции
  • Они должны быть БЫСТРЫМИ

Функциональные тесты:

  • Пишите МЕНЬШЕ
  • Проверяйте полный сценарий
  • Тестируйте реальные интеграции
  • Они медленнее, но проверяют реальность

Правило 70/20/10:

  • 70% unit тесты
  • 20% интеграционные
  • 10% E2E

Оптимальный проект имеет БОЛЬШОе количество быстрых unit тестов, среднее количество интеграционных, и минимум медленных E2E тестов.

В чем разница между unit тестами и функциональными тестами? | PrepBro