← Назад к вопросам
Как взаимодействовал с тестированием в разработке
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
Мой подход к тестированию
- TDD — пишу тест перед кодом
- Unit тесты — для каждого публичного метода
- Моки и Спай — изолирую компоненты
- Интеграционные тесты — проверяю взаимодействие
- E2E тесты — проверяю пользовательский сценарий
- Покрытие — стараюсь достичь 80%+ покрытия
- 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 пайплайны для автоматизации
Тестирование — это не задача, это образ мышления разработчика, обеспечивающий качество и надёжность кода.