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

Как взаимодействовал с тестированием в разработке

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

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

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

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

# Как взаимодействовал с тестированием в разработке

Тестирование — это неотъемлемая часть моего рабочего процесса. Я использую комплексный подход, включающий unit-тесты, интеграционные тесты и end-to-end тесты.

1. Unit Testing (JUnit + Mockito)

Основной инструмент для тестирования отдельных компонентов:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.InjectMocks;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Тесты для UserService")
public class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @Mock
    private EmailService emailService;
    
    @InjectMocks
    private UserService userService;
    
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    
    @Test
    @DisplayName("Должен создать пользователя с корректными данными")
    void testCreateUser_Success() {
        // Arrange (подготовка)
        String email = "user@example.com";
        User newUser = new User(email, "password123");
        User savedUser = new User(1L, email, "password123");
        
        when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
        when(userRepository.save(any(User.class))).thenReturn(savedUser);
        
        // Act (выполнение)
        User result = userService.createUser(email, "password123");
        
        // Assert (проверка)
        assertNotNull(result);
        assertEquals(1L, result.getId());
        assertEquals(email, result.getEmail());
        verify(emailService, times(1)).sendWelcomeEmail(email);
    }
    
    @Test
    @DisplayName("Должен выбросить исключение при дублировании email")
    void testCreateUser_DuplicateEmail() {
        // Arrange
        String email = "existing@example.com";
        when(userRepository.findByEmail(email)).thenReturn(
            Optional.of(new User(1L, email, "password"))
        );
        
        // Act & Assert
        assertThrows(DuplicateEmailException.class, () -> {
            userService.createUser(email, "password123");
        });
        
        verify(emailService, never()).sendWelcomeEmail(any());
    }
}

2. Integration Testing (Spring Boot Test)

Тестирование взаимодействия компонентов:

import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.transaction.annotation.Transactional;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Transactional  // откатывает изменения после каждого теста
public class UserControllerIntegrationTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    @DisplayName("POST /api/users должен создать пользователя")
    void testCreateUserEndpoint() throws Exception {
        // Arrange
        String requestBody = """
            {
                "email": "newuser@example.com",
                "password": "SecurePassword123"
            }
        """;
        
        // Act & Assert
        MvcResult result = mockMvc.perform(
            post("/api/users")
                .contentType("application/json")
                .content(requestBody)
        )
        .andExpect(status().isCreated())
        .andExpect(jsonPath("$.id").isNumber())
        .andExpect(jsonPath("$.email").value("newuser@example.com"))
        .andReturn();
        
        // Проверяем, что пользователь действительно сохранён
        assertTrue(userRepository.findByEmail("newuser@example.com").isPresent());
    }
    
    @Test
    void testGetUserById() throws Exception {
        // Arrange - создаём пользователя в БД
        User user = new User(null, "test@example.com", "password");
        User savedUser = userRepository.save(user);
        
        // Act & Assert
        mockMvc.perform(get("/api/users/" + savedUser.getId()))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.email").value("test@example.com"));
    }
}

3. Test Driven Development (TDD)

Писание тестов перед кодом:

public class PaymentCalculatorTest {
    
    private PaymentCalculator calculator;
    
    @BeforeEach
    void setUp() {
        calculator = new PaymentCalculator();
    }
    
    // Шаг 1: RED — тест падает
    @Test
    void testCalculateDiscount_10PercentFor100Items() {
        double price = 100.0;
        int quantity = 100;
        
        double result = calculator.calculateFinalPrice(price, quantity);
        
        assertEquals(9000.0, result); // 100 * 100 * (1 - 0.10)
    }
    
    // Шаг 2: GREEN — минимальный код
    // public double calculateFinalPrice(double price, int quantity) {
    //     if (quantity >= 100) {
    //         return price * quantity * 0.9; // 10% скидка
    //     }
    //     return price * quantity;
    // }
    
    // Шаг 3: REFACTOR — улучшение кода
}

4. Тестирование с TestContainers (для БД)

Использование реальных контейнеров в тестах:

import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@SpringBootTest
public class UserRepositoryContainerTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
        .withDatabaseName("testdb")
        .withUsername("testuser")
        .withPassword("testpass");
    
    @Autowired
    private UserRepository userRepository;
    
    @BeforeAll
    static void beforeAll() {
        System.setProperty("spring.datasource.url", postgres.getJdbcUrl());
        System.setProperty("spring.datasource.username", postgres.getUsername());
        System.setProperty("spring.datasource.password", postgres.getPassword());
    }
    
    @Test
    void testSaveAndFindUser() {
        // Arrange
        User user = new User(null, "test@example.com", "password");
        
        // Act
        User saved = userRepository.save(user);
        
        // Assert
        assertTrue(userRepository.findByEmail("test@example.com").isPresent());
    }
}

5. Параметризованные тесты

Тестирование с несколькими наборами данных:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

public class DiscountCalculatorTest {
    
    private DiscountCalculator calculator = new DiscountCalculator();
    
    @ParameterizedTest
    @CsvSource({
        "100, 5, 0",      // кол-во < 10 → скидка 0%
        "100, 10, 0.05",   // кол-во 10-49 → скидка 5%
        "100, 50, 0.10",   // кол-во 50-99 → скидка 10%
        "100, 100, 0.15"   // кол-во >= 100 → скидка 15%
    })
    void testDiscountByQuantity(double price, int qty, double expectedDiscount) {
        double result = calculator.applyDiscount(price, qty);
        double expected = price * qty * (1 - expectedDiscount);
        assertEquals(expected, result, 0.01);
    }
}

6. End-to-End тестирование (Selenium/Playwright)

Для веб-приложений:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.By;
import org.junit.jupiter.api.Test;

public class UserRegistrationE2ETest {
    
    private WebDriver driver;
    
    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    }
    
    @Test
    void testUserRegistrationFlow() {
        // Открываем страницу
        driver.get("http://localhost:8080/register");
        
        // Заполняем форму
        driver.findElement(By.id("email")).sendKeys("newuser@example.com");
        driver.findElement(By.id("password")).sendKeys("SecurePass123");
        driver.findElement(By.id("confirmPassword")).sendKeys("SecurePass123");
        
        // Отправляем форму
        driver.findElement(By.id("submitBtn")).click();
        
        // Проверяем результат
        String pageTitle = driver.getTitle();
        assertEquals("Registration Success", pageTitle);
    }
    
    @AfterEach
    void tearDown() {
        driver.quit();
    }
}

7. Покрытие тестами (Coverage)

Обеспечение достаточного покрытия кода:

# Maven с JaCoCo
mvn clean test jacoco:report

# Просмотр отчёта
# target/site/jacoco/index.html

8. CI/CD интеграция

Автоматический запуск тестов:

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-java@v2
        with:
          java-version: '17'
      - run: mvn clean test
      - run: mvn jacoco:report

Мой подход к тестированию

  1. TDD — пишу тест перед кодом
  2. Unit тесты — для каждого публичного метода
  3. Моки и Спай — изолирую компоненты
  4. Интеграционные тесты — проверяю взаимодействие
  5. E2E тесты — проверяю пользовательский сценарий
  6. Покрытие — стараюсь достичь 80%+ покрытия
  7. CI/CD — автоматический запуск при каждом commit

Типичная структура проекта

src/
  main/
    java/
      com/example/
        service/UserService.java
        repository/UserRepository.java
  test/
    java/
      com/example/
        service/UserServiceTest.java
        repository/UserRepositoryTest.java
        integration/UserControllerIntegrationTest.java

Резюме

В разработке я активно использую:

  • JUnit 5 и Mockito для unit-тестов
  • Spring Test для интеграционного тестирования
  • TestContainers для работы с БД
  • TDD методологию для качественного кода
  • CI/CD пайплайны для автоматизации

Тестирование — это не задача, это образ мышления разработчика, обеспечивающий качество и надёжность кода.