Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие сценарии тестирования проверял?
В профессиональной разработке на Java мы проверяем множество сценариев тестирования, используя различные уровни тестирования (unit, integration, end-to-end) и разные подходы к тестированию. Вот основные сценарии и практики, которые я применял:
1. Unit тестирование (JUnit + Mockito)
Тестирование успешных случаев
@Test
public void testCalculateTaxForValidIncome() {
// Arrange
TaxCalculator calculator = new TaxCalculator();
double income = 50000.0;
double expectedTax = 10000.0; // 20% tax
// Act
double actualTax = calculator.calculateTax(income);
// Assert
assertEquals(expectedTax, actualTax, 0.01);
}
Тестирование граничных значений (Boundary Testing)
@Test
public void testWithZeroIncome() {
TaxCalculator calculator = new TaxCalculator();
assertEquals(0.0, calculator.calculateTax(0.0), 0.01);
}
@Test
public void testWithMaximumIncome() {
TaxCalculator calculator = new TaxCalculator();
double maxIncome = Double.MAX_VALUE;
// Проверяем, что не выбросится исключение
assertDoesNotThrow(() -> calculator.calculateTax(maxIncome));
}
Тестирование исключений
@Test
public void testNegativeIncomeThrowsException() {
TaxCalculator calculator = new TaxCalculator();
// Assert что будет выброшено исключение
assertThrows(IllegalArgumentException.class, () -> {
calculator.calculateTax(-1000.0);
});
}
@Test
public void testNullInputThrowsNullPointerException() {
UserService service = new UserService();
assertThrows(NullPointerException.class, () -> {
service.createUser(null);
});
}
Тестирование с Mockito (мокирование зависимостей)
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService; // Инъектируем моки
@Test
public void testCreateUserSendsWelcomeEmail() {
// Arrange
User newUser = new User("john@example.com");
when(userRepository.save(any(User.class))).thenReturn(newUser);
// Act
userService.createUser(newUser);
// Assert
verify(emailService, times(1)).sendWelcomeEmail(newUser.getEmail());
verify(userRepository, times(1)).save(newUser);
}
}
2. Integration тестирование
Тестирование с реальной БД
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@Test
@Transactional
public void testCreateUserThroughAPI() throws Exception {
String userJson = "{\"email\": \"test@example.com\", \"name\": \"John\"}
";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.email").value("test@example.com"));
// Проверяем, что пользователь действительно сохранён в БД
User savedUser = userRepository.findByEmail("test@example.com");
assertNotNull(savedUser);
assertEquals("John", savedUser.getName());
}
}
Тестирование сценариев с несколькими операциями
@SpringBootTest
public class OrderProcessingIntegrationTest {
@Autowired
private OrderService orderService;
@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
@Test
@Transactional
public void testCompleteOrderProcessing() {
// 1. Проверяем наличие товара
assertTrue(inventoryService.isAvailable("PRODUCT_123", 5));
// 2. Создаём заказ
Order order = orderService.createOrder("USER_1", "PRODUCT_123", 5);
assertNotNull(order.getId());
// 3. Проверяем платёж
PaymentResult payment = paymentService.processPayment(order);
assertTrue(payment.isSuccessful());
// 4. Уменьшаем инвентарь
inventoryService.decreaseStock("PRODUCT_123", 5);
// 5. Проверяем финальное состояние
Order completedOrder = orderService.getOrder(order.getId());
assertEquals(OrderStatus.COMPLETED, completedOrder.getStatus());
assertEquals(0, inventoryService.getStock("PRODUCT_123"));
}
}
3. Параметризованное тестирование (Parameterized Tests)
public class MathOperationsTest {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
public void testIsPositive(int number) {
assertTrue(number > 0);
}
@ParameterizedTest
@CsvSource({
"10, 5, 15", // add(10, 5) = 15
"0, 0, 0", // add(0, 0) = 0
"-5, 5, 0", // add(-5, 5) = 0
"100, -50, 50" // add(100, -50) = 50
})
public void testAdditionWithMultipleInputs(int a, int b, int expected) {
Calculator calc = new Calculator();
assertEquals(expected, calc.add(a, b));
}
@ParameterizedTest
@MethodSource("provideTestData")
public void testWithComplexData(String input, String expected) {
assertEquals(expected, processInput(input));
}
private static Stream<Arguments> provideTestData() {
return Stream.of(
Arguments.of("hello", "HELLO"),
Arguments.of("world", "WORLD"),
Arguments.of("", "")
);
}
}
4. Тестирование обработки ошибок и исключений
@Test
public void testDataValidationScenarios() {
UserValidator validator = new UserValidator();
// Пустое имя
ValidationResult result1 = validator.validate(new User("", "test@example.com"));
assertFalse(result1.isValid());
assertTrue(result1.getErrors().contains("Name cannot be empty"));
// Неправильный email
ValidationResult result2 = validator.validate(new User("John", "invalid-email"));
assertFalse(result2.isValid());
assertTrue(result2.getErrors().contains("Invalid email format"));
}
@Test
public void testConcurrentAccessScenario() throws InterruptedException {
AtomicInteger counter = new AtomicInteger(0);
CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
counter.incrementAndGet();
latch.countDown();
}).start();
}
latch.await();
assertEquals(10, counter.get());
}
5. Тестирование REST API
@SpringBootTest
@AutoConfigureMockMvc
public class UserAPITest {
@Autowired
private MockMvc mockMvc;
@Test
public void testGetUserByIdSuccess() throws Exception {
mockMvc.perform(get("/api/users/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").exists());
}
@Test
public void testGetUserByIdNotFound() throws Exception {
mockMvc.perform(get("/api/users/999")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
@Test
public void testCreateUserValidation() throws Exception {
String invalidUserJson = "{\"name\": \"\"}";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidUserJson))
.andExpect(status().isBadRequest());
}
}
6. Тестирование многопоточности и Race Conditions
public class ThreadSafetyTest {
@Test
public void testConcurrentListModification() throws InterruptedException {
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
int threadCount = 100;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
final int value = i;
new Thread(() -> {
list.add(value);
latch.countDown();
}).start();
}
latch.await();
assertEquals(threadCount, list.size());
}
@Test
public void testDeadlockScenario() throws InterruptedException {
Object lock1 = new Object();
Object lock2 = new Object();
AtomicBoolean completed = new AtomicBoolean(false);
Thread t1 = new Thread(() -> {
synchronized(lock1) {
try { Thread.sleep(10); } catch (InterruptedException e) {}
synchronized(lock2) {
completed.set(true);
}
}
});
Thread t2 = new Thread(() -> {
synchronized(lock2) {
try { Thread.sleep(10); } catch (InterruptedException e) {}
synchronized(lock1) {
completed.set(true);
}
}
});
t1.start();
t2.start();
// Ждём с таймаутом (deadlock не произойдёт, если код правильный)
t1.join(1000);
t2.join(1000);
assertFalse(t1.isAlive());
assertFalse(t2.isAlive());
}
}
7. Тестирование производительности
@Test
public void testAlgorithmPerformance() {
List<Integer> largeList = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
largeList.add(i);
}
long startTime = System.currentTimeMillis();
// Функция, которую тестируем
int result = largeList.stream()
.filter(n -> n % 2 == 0)
.mapToInt(Integer::intValue)
.sum();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
// Должно выполниться менее чем за 1 секунду
assertTrue(duration < 1000, "Algorithm took too long: " + duration + "ms");
}
8. Тестирование состояния (State Testing)
public class StateMachineTest {
@Test
public void testOrderStateTransitions() {
Order order = new Order();
assertEquals(OrderState.PENDING, order.getState());
// Переход 1: PENDING → CONFIRMED
order.confirm();
assertEquals(OrderState.CONFIRMED, order.getState());
// Переход 2: CONFIRMED → SHIPPED
order.ship();
assertEquals(OrderState.SHIPPED, order.getState());
// Переход 3: SHIPPED → DELIVERED
order.deliver();
assertEquals(OrderState.DELIVERED, order.getState());
// Нельзя перейти в CONFIRMED из DELIVERED
assertThrows(IllegalStateException.class, order::confirm);
}
}
9. Сценарии с VCR (запись HTTP запросов)
@ExtendWith(VcrExtension.class)
@Cassette("src/test/resources/cassettes/github_api.yaml")
public class ExternalAPITest {
@Test
public void testGitHubAPICall() {
GitHubClient client = new GitHubClient();
// При первом запуске — реальный запрос, записывается в cassette
// При последующих запусках — используется записанный ответ
Repository repo = client.getRepository("torvalds", "linux");
assertNotNull(repo);
assertEquals("linux", repo.getName());
}
}
10. Coverage и контроль качества
# Запуск тестов с отчётом о покрытии
mvn clean test jacoco:report
# JaCoCo требует минимум 80-90% покрытия
# Проверяем в CI/CD pipeline
Итоговое резюме тестовых сценариев
| Тип | Примеры | Инструменты |
|---|---|---|
| Unit | Бизнес-логика, валидация | JUnit, Mockito |
| Integration | API, БД, сервисы | TestContainers, MockMvc |
| E2E | Полный user flow | Selenium, Playwright |
| Performance | Нагрузочное тестирование | JMH, Gatling |
| Security | SQL Injection, CSRF | OWASP ZAP, JWTk |
| Boundary | Граничные значения | Parameterized tests |
| Exception | Обработка ошибок | assertThrows |
| Concurrency | Многопоточность | CountDownLatch, Thread |
В профессиональной разработке на Java мы используем эти сценарии комплексно для обеспечения высокого качества кода.