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

Какие тесты должен писать разработчик

2.0 Middle🔥 191 комментариев
#Тестирование

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

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

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

Типы тестов: стратегия тестирования для Java разработчика

Тестирование — критическая часть разработки, которая гарантирует качество кода и уверенность в изменениях. Разработчик должен писать разные типы тестов для разного уровня покрытия.

1. Unit тесты (юнит-тесты)

Что это: Тесты отдельных методов/функций в изоляции.

Характеристики:

  • Тестируют одно поведение за раз
  • Быстрые (миллисекунды)
  • Mockируют все зависимости
  • Запускаются часто (на каждое изменение)
public class CalculatorTest {
    
    private Calculator calculator;
    
    @Before
    public void setUp() {
        calculator = new Calculator();
    }
    
    @Test
    public void testAddition() {
        // Arrange
        int a = 5;
        int b = 3;
        
        // Act
        int result = calculator.add(a, b);
        
        // Assert
        assertEquals(8, result);
    }
    
    @Test
    public void testDivisionByZero() {
        assertThrows(IllegalArgumentException.class, 
            () -> calculator.divide(10, 0));
    }
}

Инструменты: JUnit, TestNG, Mockito, AssertJ

Преимущества:

  • Быстрая обратная связь при разработке
  • Документирует поведение кода
  • Упрощает рефакторинг
  • Помогает выявить баги на ранней стадии

Coverage: 60-80% кода

2. Integration тесты

Что это: Тесты взаимодействия между компонентами (БД, API, очереди).

Характеристики:

  • Используют реальные или тестовые БД
  • Медленнее (секунды)
  • Минимизируют mocking
  • Проверяют contracts между сервисами
@SpringBootTest
@ActiveProfiles("test")
public class UserServiceIntegrationTest {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    public void testCreateAndFetchUser() {
        // Arrange
        User newUser = new User("john", "john@example.com");
        
        // Act
        User savedUser = userService.createUser(newUser);
        User fetchedUser = userRepository.findById(savedUser.getId())
            .orElseThrow();
        
        // Assert
        assertEquals("john", fetchedUser.getUsername());
    }
    
    @Test
    public void testTransactionRollback() {
        // Проверяем, что при ошибке транзакция откатывается
        assertThrows(RuntimeException.class, 
            () -> userService.createInvalidUser());
        
        assertEquals(0, userRepository.count());
    }
}

Инструменты: Testcontainers, Spring Boot Test, WireMock

Преимущества:

  • Проверяют реальное взаимодействие
  • Ловят проблемы, которые юнит тесты пропускают
  • Тестируют интеграцию с БД и внешними сервисами

Coverage: 20-40% кода

3. End-to-End (E2E) тесты

Что это: Тесты полного потока пользователя через весь stack.

Характеристики:

  • Тестируют UI + Backend + БД
  • Очень медленные (минуты)
  • Запускаются редко (перед release)
  • Требуют реального окружения
// Selenium / Playwright
public class UserRegistrationE2ETest {
    
    private WebDriver driver;
    
    @Before
    public void setup() {
        driver = new ChromeDriver();
    }
    
    @Test
    public void testCompleteUserFlow() {
        // Заходим на сайт
        driver.get("http://localhost:3000");
        
        // Кликаем на регистрацию
        driver.findElement(By.id("signup-btn")).click();
        
        // Заполняем форму
        driver.findElement(By.name("username")).sendKeys("newuser");
        driver.findElement(By.name("email")).sendKeys("user@example.com");
        driver.findElement(By.name("password")).sendKeys("SecurePass123");
        
        // Подтверждаем
        driver.findElement(By.id("register-btn")).click();
        
        // Проверяем, что пришли на главную
        WebDriverWait wait = new WebDriverWait(driver, 10);
        wait.until(ExpectedConditions.presenceOfElementLocated(
            By.id("dashboard")));
        
        assertTrue(driver.getCurrentUrl().contains("dashboard"));
    }
}

Инструменты: Selenium, Cypress, Playwright

Преимущества:

  • Проверяют весь user journey
  • Ловят баги на границах слоёв

Недостатки:

  • Очень медленные
  • Хрупкие (часто сломаются)
  • Дорогие в поддержке
  • Запускать редко

4. Performance тесты

Что это: Тесты проверяющие производительность под нагрузкой.

Характеристики:

  • Проверяют latency, throughput
  • Моделируют высокую нагрузку
  • Запускаются перед release
  • Специальные инструменты
// JMH (Java Microbenchmark Harness)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class ListPerformanceBenchmark {
    
    private List<Integer> arrayList;
    private List<Integer> linkedList;
    
    @Setup
    public void setup() {
        arrayList = new ArrayList<>(100000);
        linkedList = new LinkedList<>();
        
        for (int i = 0; i < 100000; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }
    
    @Benchmark
    public int accessArrayList() {
        return arrayList.get(50000);
    }
    
    @Benchmark
    public int accessLinkedList() {
        return linkedList.get(50000);
    }
}

Инструменты: JMH, Gatling, Apache JMeter, Locust

5. Security тесты

Что это: Тесты проверяющие уязвимости и безопасность.

Примеры:

  • SQL Injection
  • XSS атаки
  • Authentication/Authorization
  • CSRF защита
public class SecurityTest {
    
    @Test
    public void testSQLInjectionProtection() {
        String userInput = "'; DROP TABLE users; --";
        
        // Используем prepared statements
        String query = "SELECT * FROM users WHERE id = ?";
        
        // Это безопасно, userInput не может сломать query
        List<User> users = repository.findByCustomQuery(query, userInput);
        
        assertEquals(0, users.size());
    }
    
    @Test
    public void testUnauthorizedAccess() {
        // Без auth token не должна быть доступна
        ResponseEntity<String> response = 
            restTemplate.getForEntity("/api/admin", String.class);
        
        assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
    }
}

6. Smoke тесты

Что это: Минимальные тесты проверяющие, что основной функционал работает.

Характеристики:

  • Быстрые (максимум 10 минут)
  • Проверяют только критичные пути
  • Запускаются после каждого deploy
  • Дают быструю обратную связь
public class SmokeTest {
    
    @Test
    public void testServerIsUp() {
        ResponseEntity<String> response = 
            restTemplate.getForEntity("http://localhost:8080/health", 
                String.class);
        assertEquals(HttpStatus.OK, response.getStatusCode());
    }
    
    @Test
    public void testDatabaseConnection() {
        User user = userRepository.findById(1L).orElseThrow();
        assertNotNull(user);
    }
}

Pyramid тестирования (Test Pyramid)

        /\        E2E тесты (5-10%)
       /  \       Медленные, хрупкие, дорогие
      /    \
     /      \    Integration тесты (20-30%)
    /        \   Средняя скорость, проверяют взаимодействие
   /          \
  /            \ Unit тесты (60-80%)
 /______________\Быстрые, дешёвые, много

Что должен писать разработчик

ОБЯЗАТЕЛЬНО:

  1. Unit тесты для всей бизнес-логики

    • Каждый публичный метод
    • Happy path и edge cases
    • Error handling
  2. Integration тесты для:

    • Взаимодействия с БД
    • API endpoints
    • Очереди сообщений
  3. Smoke тесты для критичного функционала

ЖЕЛАТЕЛЬНО: 4. E2E тесты для основных user journeys (но не всех) 5. Security тесты для чувствительных операций 6. Performance тесты для критичных операций

Рекомендуемое покрытие

  • Code Coverage: минимум 80%, идеально 90%+
  • Critical path coverage: 100%
  • Branch coverage: 70%+

Best Practices

1. TDD (Test-Driven Development)

RED → GREEN → REFACTOR
Сначала пиши тест, потом код

2. AAA паттерн

@Test
public void testSomething() {
    // Arrange: подготовь данные
    User user = new User("john");
    
    // Act: выполни действие
    boolean isValid = user.validate();
    
    // Assert: проверь результат
    assertTrue(isValid);
}

3. Один assert на тест (когда возможно)

  • Упрощает понимание
  • Быстрее найти проблему
  • Иногда несколько asserts OK

4. Meaningful names

// Плохо
@Test
public void test1() { }

// Хорошо
@Test
public void shouldThrowExceptionWhenUserIsNull() { }

5. Не тестируй внутреннее состояние (state-based)

// Плохо: тестируешь внутреннее состояние
User user = new User();
user.setName("john");
assertEquals("john", user.getName());

// Хорошо: тестируешь поведение
User user = new User();
assertTrue(user.isValid());

Правильная стратегия тестирования — это баланс между скоростью разработки, уверенностью в коде и стоимостью поддержки тестов.

Какие тесты должен писать разработчик | PrepBro