https://www.youtube.com/watch?v=XSkz0kO7J3w
해당 영상을 보고 작성한 글입니다.
Test 코드를 작성하다보면 여러 인프라스트럭처 빈을 의존하고 있는 경우 상당히 많은 모킹 작업이 필요합니다. 이러다보니 테스트 코드 자체가 많이 더러워지기도 하고 관리가 어려워지는 경우가 있습니다.
어쩌다가 보게된 2023 Spring Camp 유튜브 영상에서는 이런 상황에 대하여 좋은 해결책을 제시해주었습니다.

바로 새로운 객체를 추가하여 책임을 분산시키는 방법입니다.
예시를 보겠습니다. 주문에 필요한 정보들을 여러 인프라스트럭처에서 조회한 이후 비즈니스 로직을 실행하고 있습니다.
@Service
public class OrderService {
private final ProductQueryService productQueryService;
private final ExchangeRateClient exchangeRateClient;
private final CouponQueryService couponQueryService;
private final ShwoQueryService shopQueryService;
public Order order(Long productId, LocalDate orderDate, BigDecimal orderAmount, Long shopId, String couponCode) {
Product product = productQueryService.findById(productId);
ExchangeRateResponse exchangeRateResponse = exchangeRateClient.getExchangeRate(orderDate, "USD", "KRW");
Coupon coupon = couponQueryService.findByCode(couponCode);
Shop shop = shopQueryService.findById(shopId);
/**
* 복잡한 로직..
* ...
*/
Order order = save(order);
return order;
}
}
영상에서는 해당 클래스의 책임을 아래와 같이 분리하는 것을 제안합니다.

OrderServiceSupport 는 Spring Bean Context 와 인프라스트럭처의 관련 코드가 없는 순수한 POJO 로 구성되고, 조회에 대한 책임 없이 복잡한 로직에 대한 책임만 갖습니다.
@Service
public class OrderServiceSupport {
private final ProductQueryService productQueryService;
private final ExchangeRateClient exchangeRateClient;
private final CouponQueryService couponQueryService;
private final ShwoQueryService shopQueryService;
public Order order(Product product,
LocalDate orderDate,
BigDecimal orderAmount,
ExchangeRateResponse exchangeRateResponse,
Shop shop,
Coupon coupon) {
/**
* 복잡한 로직..
* ...
*/
Order order = save(order);
return order;
}
}
기존 OrderService 는 데이터 조회의 책임만 갖습니다.
@Service
public class OrderService {
private final ProductQueryService productQueryService;
private final ExchangeRateClient exchangeRateClient;
private final CouponQueryService couponQueryService;
private final ShwoQueryService shopQueryService;
public Order order(Long productId, LocalDate orderDate, BigDecimal orderAmount, Long shopId, String couponCode) {
Product product = productQueryService.findById(productId);
ExchangeRateResponse exchangeRateResponse = exchangeRateClient.getExchangeRate(orderDate, "USD", "KRW");
Coupon coupon = couponQueryService.findByCode(couponCode);
Shop shop = shopQueryService.findById(shopId);
Order order = orderServiceSupport.order();
return order;
}
}
이렇게 책임을 분리한경우 OrderServiceSupport 에서는 복잡한 비즈니스 로직에 대한 테스트를 POJO 객체로만 테스트 할 수 있습니다. 혹시라도 인프라스트럭쳐가 변경되더라도 POJO 에 대한 테스트 로직을 수정할 필요는 없습니다.
public class OrderServiceSupportTest {
@Test
void 쿠폰_적용_없는_주문_생성() {
// given
Product product = new Product();
LocalDate orderDate = LocalDate.of(2023, 11, 10);
BigDecimal orderAmount = BigDecimal.valueOf(10000);
ExcahngeRateResponse excahngeRateResponse = new ExchangeRateResponse();
Shop shop = new Shop();
// when
Order order = orderServiceSupport.order(product, orderDate, orderAmount, exchangeRateResponse, shop, null);
// then
assertThat(order)...
}
}
OrderServiceSupport 에서 꼼꼼하게 테스트로직을 작성했다고 가정한 경우 OrderService 에서는 상대적으로 복잡한 테스트가 필요하지 않을것 입니다.