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

Что такое Critical Path Testing?

1.7 Middle🔥 161 комментариев
#Тестирование

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

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

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

Critical Path Testing: тестирование критических путей

Critical Path Testing (CPT) — это методология тестирования, которая сосредотачивает усилия на критических путях выполнения приложения — последовательностях операций, которые являются наиболее важными для бизнеса и пользователей. Это путь, на котором задержка может оказать наибольшее влияние на пользовательский опыт и бизнес-результаты.

Основная идея

За одну сессию пользователя обычно есть несколько путей выполнения: основной путь (критический) и альтернативные пути. Critical Path Testing фокусируется на:

  1. Основном пути — самый частый сценарий использования
  2. Самом медленном пути — сценарий, требующий больше операций
  3. Пути с наибольшим бизнес-влиянием — где ошибка приводит к потере денег

Примеры критических путей

Для e-commerce приложения:

  1. Критический путь 1: Оформление покупки (высокое бизнес-влияние)

    • Поиск товара → Добавление в корзину → Оформление заказа → Платёж → Подтверждение
  2. Критический путь 2: Аутентификация (высокое использование)

    • Ввод логина → Ввод пароля → Двухфакторная аутентификация → Вход в систему
  3. Некритический путь: Обновление профиля пользователя

Практический пример на 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

  1. Идентифицируй критические пути

    // Помечай тесты
    @Tag("critical-path")
    @Test
    void testCriticalPaymentFlow() { }
    
  2. Протестируй graceful degradation

    • Что происходит при ошибке БД?
    • Что происходит при ошибке платёжного шлюза?
  3. Используй компенсирующие транзакции

    @Transactional
    public void processPaymentWithCompensation(Payment payment) {
        try {
            chargeCard(payment);
        } catch (PaymentException e) {
            compensate(payment); // Отката денег
            throw new PaymentFailedException(e);
        }
    }
    
  4. Мониторь критические пути в 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 — это обязательность.