멀티 스레드 테스트 코드
💡StockCurrencyTest.java
package com.hubEleven.product.stock.application.service;
import static org.assertj.core.api.Assertions.assertThat;
import com.hubEleven.product.domain.model.Product;
import com.hubEleven.product.infrastructure.repository.JpaProductRepository;
import com.hubEleven.product.stock.application.fixtures.ProductFixture;
import com.hubEleven.product.stock.application.fixtures.StockFixture;
import com.hubEleven.stock.application.service.StockServiceImpl;
import com.hubEleven.stock.domain.model.Stock;
import com.hubEleven.stock.infrastructure.repository.JpaStockRepository;
import com.hubEleven.stock.presentation.dto.request.StockRequests;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class StockCurrencyTest {
@Autowired
private StockServiceImpl stockServiceImpl;
@Autowired
private JpaStockRepository jpaStockRepository;
@Autowired
private JpaProductRepository jpaProductRepository;
@BeforeEach
void setUp() {
Product p = ProductFixture.createDefault();
Product product = jpaProductRepository.saveAndFlush(p);
Stock aDefault = StockFixture.createDefault(product.getProductId());
jpaStockRepository.saveAndFlush(aDefault);
}
@AfterEach
void tearDown() {
jpaStockRepository.deleteAll();
jpaProductRepository.deleteAll();
}
@Test
@DisplayName("재고 감소 - 100개 동시 요청 시 정확히 차감")
void decreaseStock_when100ConcurrentRequests_thenSuccess() throws InterruptedException {
// given
int threadCount = 50;
int decreaseAmountPerCall = 1; // 50명이 1개씩 감소 => 성공 50이면 100-50 = 50
Product product = jpaProductRepository.findAll().get(0);
ExecutorService pool = Executors.newFixedThreadPool(threadCount);
CountDownLatch readyLatch = new CountDownLatch(threadCount);
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch doneLatch = new CountDownLatch(threadCount);
AtomicInteger successCount = new AtomicInteger();
AtomicInteger failureCount = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
pool.submit(() -> {
readyLatch.countDown();
try {
startLatch.await();
StockRequests.Decrease req =
StockFixture.decreaseRequest(decreaseAmountPerCall,
product.getProductId());
stockServiceImpl.decreaseStock(req);
successCount.incrementAndGet();
} catch (Exception e) {
// 재고부족/락 충돌/낙관적락 등 어떤 예외든 실패로 집계
failureCount.incrementAndGet();
} finally {
doneLatch.countDown();
}
});
}
// 모든 스레드 준비 -> 동시에 출발 -> 종료 대기
readyLatch.await();
startLatch.countDown();
doneLatch.await();
pool.shutdown();
// then
Stock updated =
jpaStockRepository.findAll().get(0);
int initial = 100;
assertThat(updated.getQuantity()).isEqualTo(50);
}
}
'Projects > HubEleven' 카테고리의 다른 글
| [동시성 처리] 재고 감소 통합 테스트 코드 6 - test DB (0) | 2026.01.31 |
|---|---|
| [동시성 처리] 재고 감소 통합 테스트 코드 5 - 단일 스레드 (업데이트 중..) (0) | 2026.01.30 |
| [동시성 처리] 재고 감소 통합 테스트 코드 4 - 단일 스레드 (0) | 2026.01.29 |
| [리팩토링] 권한 별 기능 제한 로직 구현 (3) (업데이트 중..) (0) | 2026.01.16 |
| [리팩토링] 공통 모듈 수정으로 인한 리팩토링 (0) | 2026.01.15 |