Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Critical Path Testing: тестирование критических путей
Critical Path Testing (CPT) — это методология тестирования, которая сосредотачивает усилия на критических путях выполнения приложения — последовательностях операций, которые являются наиболее важными для бизнеса и пользователей. Это путь, на котором задержка может оказать наибольшее влияние на пользовательский опыт и бизнес-результаты.
Основная идея
За одну сессию пользователя обычно есть несколько путей выполнения: основной путь (критический) и альтернативные пути. Critical Path Testing фокусируется на:
- Основном пути — самый частый сценарий использования
- Самом медленном пути — сценарий, требующий больше операций
- Пути с наибольшим бизнес-влиянием — где ошибка приводит к потере денег
Примеры критических путей
Для e-commerce приложения:
-
Критический путь 1: Оформление покупки (высокое бизнес-влияние)
- Поиск товара → Добавление в корзину → Оформление заказа → Платёж → Подтверждение
-
Критический путь 2: Аутентификация (высокое использование)
- Ввод логина → Ввод пароля → Двухфакторная аутентификация → Вход в систему
-
Некритический путь: Обновление профиля пользователя
Практический пример на Java/Spring
Сценарий: Payment Processing System
// Критический путь: Обработка платежа
@RestController
@RequestMapping("/api/v1/payments")
public class PaymentController {
// Это критический путь - ошибка здесь = потеря дохода
@PostMapping("/{id}/process")
public ResponseEntity<PaymentResponse> processPayment(
@PathVariable Long id,
@RequestBody PaymentRequest request) {
// Шаг 1: Валидация платежа
if (!isValidPayment(request)) {
return ResponseEntity.badRequest().build();
}
// Шаг 2: Проверка баланса (критично)
Payment payment = paymentService.validateAndLock(id);
if (payment.getAmount() > request.getAmount()) {
return ResponseEntity.status(402).build(); // Insufficient funds
}
// Шаг 3: Обработка транзакции (критично - деньги)
PaymentResult result = paymentGateway.charge(request);
// Шаг 4: Сохранение результата (критично - для отчетности)
paymentService.save(payment, result);
// Шаг 5: Отправка уведомления пользователю
notificationService.sendConfirmation(payment);
return ResponseEntity.ok(new PaymentResponse(result));
}
// Некритический путь - можно пропустить при ошибке
@GetMapping("/{id}/analytics")
public ResponseEntity<PaymentAnalytics> getAnalytics(@PathVariable Long id) {
// Это аналитика, не критично для бизнеса
return ResponseEntity.ok(analyticsService.generate(id));
}
}
Critical Path Testing: Test Cases
@SpringBootTest
class PaymentCriticalPathTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private PaymentService paymentService;
@Autowired
private PaymentGatewayMock gateway;
// КРИТИЧЕСКИЙ ПУТЬ: Успешный платёж
@Test
@DisplayName("CP1: Успешная обработка платежа")
void testSuccessfulPaymentProcessing() {
// Arrange
Long paymentId = 1L;
PaymentRequest request = new PaymentRequest();
request.setAmount(100.0);
request.setCurrency("USD");
request.setCardToken("tok_visa");
// Act
ResponseEntity<PaymentResponse> response = restTemplate.postForEntity(
"/api/v1/payments/" + paymentId + "/process",
request,
PaymentResponse.class
);
// Assert
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getStatus()).isEqualTo("COMPLETED");
assertThat(response.getBody().getTransactionId()).isNotNull();
// Verify: платёж сохранён в БД
Payment saved = paymentService.findById(paymentId);
assertThat(saved.getStatus()).isEqualTo("COMPLETED");
}
// КРИТИЧЕСКИЙ ПУТЬ: Обработка ошибки платежа
@Test
@DisplayName("CP2: Платёж отклонён - недостаточно средств")
void testPaymentDeclinedInsufficientFunds() {
// Arrange
Long paymentId = 1L;
PaymentRequest request = new PaymentRequest();
request.setAmount(1000.0); // Больше баланса
// Mock: мало денег
gateway.respondWith(PaymentGatewayResponse.builder()
.status("DECLINED")
.code("insufficient_funds")
.build());
// Act
ResponseEntity<PaymentResponse> response = restTemplate.postForEntity(
"/api/v1/payments/" + paymentId + "/process",
request,
PaymentResponse.class
);
// Assert
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.PAYMENT_REQUIRED); // 402
// Verify: платёж помечен как отклонённый
Payment saved = paymentService.findById(paymentId);
assertThat(saved.getStatus()).isEqualTo("DECLINED");
}
// КРИТИЧЕСКИЙ ПУТЬ: Обработка timeout
@Test
@DisplayName("CP3: Timeout платёжного шлюза - компенсирующая транзакция")
@Timeout(value = 5, unit = TimeUnit.SECONDS)
void testPaymentGatewayTimeout() {
// Arrange
Long paymentId = 1L;
PaymentRequest request = new PaymentRequest();
request.setAmount(100.0);
// Mock: timeout
gateway.delayResponse(10000); // 10 сек
// Act & Assert
assertThatThrownBy(() -> {
restTemplate.postForEntity(
"/api/v1/payments/" + paymentId + "/process",
request,
PaymentResponse.class
);
}).isInstanceOf(ResourceAccessException.class);
// Verify: компенсирующая транзакция (возврат денег)
Payment saved = paymentService.findById(paymentId);
assertThat(saved.getStatus()).isEqualTo("CANCELLED_DUE_TO_TIMEOUT");
}
// КРИТИЧЕСКИЙ ПУТЬ: Idempotency - дублированный платёж
@Test
@DisplayName("CP4: Повторный платёж с идентичными параметрами - idempotency")
void testPaymentIdempotency() {
// Arrange
Long paymentId = 1L;
PaymentRequest request = new PaymentRequest();
request.setIdempotencyKey("unique-123");
request.setAmount(100.0);
// Act: первый платёж
ResponseEntity<PaymentResponse> response1 = restTemplate.postForEntity(
"/api/v1/payments/" + paymentId + "/process",
request,
PaymentResponse.class
);
// Act: повторный платёж с тем же ключом
ResponseEntity<PaymentResponse> response2 = restTemplate.postForEntity(
"/api/v1/payments/" + paymentId + "/process",
request,
PaymentResponse.class
);
// Assert
assertThat(response1.getBody().getTransactionId())
.isEqualTo(response2.getBody().getTransactionId()); // Одна транзакция
// Verify: только один платёж в БД
List<Payment> payments = paymentService.findByIdempotencyKey("unique-123");
assertThat(payments).hasSize(1);
}
}
Критические пути vs обычное тестирование
| Аспект | Обычное тестирование | Critical Path Testing |
|---|---|---|
| Охват | Все функции | Только важные пути |
| Приоритет | Одинаковый | Высокий для критических |
| Фокус | Баги в любом месте | Потеря денег, data loss |
| Время | Долгое выполнение | Быстрое выполнение |
| ROI | Общее качество | Защита дохода |
Лучшие практики CPT
-
Идентифицируй критические пути
// Помечай тесты @Tag("critical-path") @Test void testCriticalPaymentFlow() { } -
Протестируй graceful degradation
- Что происходит при ошибке БД?
- Что происходит при ошибке платёжного шлюза?
-
Используй компенсирующие транзакции
@Transactional public void processPaymentWithCompensation(Payment payment) { try { chargeCard(payment); } catch (PaymentException e) { compensate(payment); // Отката денег throw new PaymentFailedException(e); } } -
Мониторь критические пути в production
@Aspect public class CriticalPathMetrics { @Around("@annotation(CriticalPath)") public Object monitor(ProceedingJoinPoint pjp) throws Throwable { Timer.Sample sample = Timer.start(); try { return pjp.proceed(); } finally { sample.stop(Timer.builder("critical_path") .tag("method", pjp.getSignature().getName()) .register(meterRegistry)); } } }
Заключение
Critical Path Testing — это стратегический подход к тестированию, который:
- Сосредотачивает ресурсы на самом важном
- Минимизирует риск потери дохода
- Экономит время на тестирование
- Обеспечивает максимальную ROI для testing effort
Для финансовых систем, e-commerce и других бизнес-критичных приложений CPT — это обязательность.