Service์ ํ์ํ ๋ชจ๋ ์ ๋ณด๋ค์ mock ์ฒ๋ฆฌ ํด์ฃผ๋ฉด ๊ฐ๋ฅํ๋ค.
์ด๊ฒ ๋ฌด์จ๋ง์ด๋? ๋ฐ๋ก ์ฝ๋๋ฅผ ๋ณด์
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Builder
public class OrderCancelReturnService {
private final DateTimeHolder dateTimeHolder;
private final RefundService refundService;
private final OrderDetailQueryRepository orderDetailQueryRepository;
private final OrderDetailCommendRepository orderDetailCommendRepository;
private final OrderQueryRepository orderQueryRepository;
private final OrderCommendRepository orderCommendRepository;
private final ProductRepository productRepository;
}
@Transactional
public CancelReturnResponseDto confirmCancelReturn(CancelRequestDto cancelRequestDto, DateTimeHolder dateTimeHolder) {
Order order = orderQueryRepository.findByOrderNo(cancelRequestDto.getOrderNo());
Integer cancelAmount = order.changeDetailsReasonAndStatus(cancelRequestDto.getCancelReason(), ORDER_CANCEL.getCode());
order.changeMemberCouponInfo(null);
RefundDto refundDto = refundService.refund(order.getTid(), cancelAmount);
return CancelReturnResponseDto.of(order, refundDto, dateTimeHolder);
}
ํ์ฌ ๋ด๊ฐ ์ฌ์ฉํ๋ Service๋ ๋ค์ ํ๋๋ค์ ํ์๋ก ํ๋ค.
์ด ๋ชจ๋ ๊ฒ์ ๋ชจํน์ ํด์ฃผ์ด์ผ ํ๋ค๋ ๋ป์ด๊ธฐ๋ ํ๋ค.
(๋ค๋ฅธ ์ด์ผ๊ธฐ์ง๋ง ํ์ฌ ์๋น์ค์์ ์ฑ
์์ด ๋ง์๋ณด์ธ๋ค. ์ด๊ฒ์ ๋์ค์ ๋ฆฌํฉํ ๋ง ํ๋๋ก ํด์ผ๊ฒ ๋ค.)
์ด๋ฐ์์ผ๋ก Fake ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฃผ์๋ค.
ํนํ ํ
์คํธ๋ฅผ ์ข ๋ ์ํํ๊ฒ ์งํํ๊ธฐ ์ํด TestContainer๋ฅผ ๋ง๋ค์๋๋ฐ ์คํ๋ง์์ ์ ๊ณตํ๋ Config๋ฅผ ์๊ฐํ๋ฉด ์ดํดํ๊ธฐ ์ฌ์ธ ๊ฒ์ด๋ค.
public class TestContainer {
public final OrderCancelReturnService orderCancelReturnService;
public final RefundService refundService;
public final OrderQueryRepository orderQueryRepository;
public final OrderCommendRepository orderCommendRepository;
public final OrderDetailQueryRepository orderDetailQueryRepository;
public final OrderDetailCommendRepository orderDetailCommendRepository;
public final ProductRepository productRepository;
public final CouponRepository couponRepository;
public final MemberCouponRepository memberCouponRepository;
@Builder
public TestContainer(DateTimeHolder dateTimeHolder) {
this.orderQueryRepository = new FakeOrderQueryRepository();
this.orderCommendRepository = new FakeOrderCommendRepository();
this.orderDetailQueryRepository = new FakeOrderDetailQueryRepository();
this.orderDetailCommendRepository = new FakeOrderDetailCommendRepository();
this.productRepository = new FakeProductRepository();
this.couponRepository = new FakeCouponRepository();
this.memberCouponRepository = new FakeMemberCouponRepository();
this.refundService = new FakeRefundService(dateTimeHolder);
this.orderCancelReturnService = OrderCancelReturnService.builder()
.dateTimeHolder(dateTimeHolder)
.orderDetailQueryRepository(this.orderDetailQueryRepository)
.orderQueryRepository(this.orderQueryRepository)
.productRepository(this.productRepository)
.refundService(this.refundService)
.orderCommendRepository(this.orderCommendRepository)
.orderDetailCommendRepository(this.orderDetailCommendRepository)
.build();
}
}
์ข ๋ ์ฝ๋๋ด์ผ๋ก ๋ค์ด๊ฐ ๋ณด์
public class FakeOrderDetailCommendRepository implements OrderDetailCommendRepository {
private Long autoGeneratedId = 0L;
private List<OrderDetail> data = new ArrayList<>();
@Override
public void save(OrderDetail orderDetail) {
if (orderDetail.getId() == null || orderDetail.getId() == 0) {
ReflectionTestUtils.setField(orderDetail, "id", ++autoGeneratedId, Long.class);
data.add(orderDetail);
} else {
data.removeIf(item -> Objects.equals(item.getId(), orderDetail.getId()));
data.add(orderDetail);
}
}
}
์ด๋ฐ์์ผ๋ก Repository๋ฅผ ๋ฉ๋ชจ๋ฆฌ ์์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ ํ ์ ์๋๋ก ํ๋ฉด ๋๋ค.
์ด๋ ๋์์ฑ ๋ฌธ์ ์ ๋ํด ๊ถ๊ธํ ์ฌ๋์ด ์์ ์ ์๋๋ฐ
ํ์ฌ ์ด ์ฝ๋๋ ํ
์คํธ์์ ๋ฉ์๋ ๋จ์๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋์์ฑ์ ๊ณ ๋ คํ์ง ์๊ณ Long, List๋ฅผ ์ฌ์ฉํ์๋ค. ๋ง์ฝ ๋์์ฑ ๋ฌธ์ ๊ฐ ๊ฑฑ์ ์ด ๋๋ค๋ฉด
AtomicLong, List<..> data = Collections.synchronizedList(new ArrayList<>(); ์ด๋ฐ๊ฒ๋ค์ ์ฐพ์๋ณด๊ณ ๊ณต๋ถํด๋ณด๊ธธ ๋ฐ๋๋ค.
class OrderCancelReturnServiceTest {
private OrderCancelReturnService orderCancelReturnService;
LocalDateTime cancelDate = LocalDateTime.of(2024, 1, 31, 9, 26);
@BeforeEach
void init() {
TestContainer testContainer = TestContainer.builder()
.dateTimeHolder(TestDateTimeHolder.builder()
.now(cancelDate)
.build())
.build();
this.orderCancelReturnService = testContainer.orderCancelReturnService;
Product product1 = Product.builder()
.name("๋นต๋นต์ดํค๋ง")
.productNo("1")
.price(1000L)
.thumbImg("๋นต๋นต์ด์ธ๋ค์ผ")
.stock(5L)
.build();
Product product2 = Product.builder()
.name("์ฅ์ง์ํค๋ง")
.productNo("2")
.price(2000L)
.thumbImg("์ฅ์ง์์ธ๋ค์ผ")
.stock(5L)
.build();
Coupon coupon1 = Coupon.builder()
.name("๋นต๋นต์ดํค๋ง์ฟ ํฐ")
.product(product1)
.price(1000L)
.count(10L)
.build();
Coupon coupon2 = Coupon.builder()
.name("์ฅ์ง์ํค๋ง์ฟ ํฐ")
.product(product2)
.price(2000L)
.count(10L)
.build();
Order order = Order.builder()
.orderNo("1")
.tid("1")
.totalDiscount(0L)
.totalPrice(8000L)
.realPrice(8000L)
.build();
LocalDateTime cancelledAt = LocalDateTime.of(2024, 1, 31, 3, 33);
OrderDetail orderDetail1 = OrderDetail.builder()
.cancelledAt(cancelledAt)
.quantity(2L)
.product(product1)
.price(1000L)
.coupon(coupon1)
.order(order)
.reason("๋จ์๋ณ์ฌ")
.tid("1")
.build();
OrderDetail orderDetail2 = OrderDetail.builder()
.cancelledAt(cancelledAt)
.quantity(3L)
.product(product2)
.price(2000L)
.coupon(coupon2)
.order(order)
.reason("๋จ์๋ณ์ฌ")
.tid("1")
.build();
order.addOrderDetail(orderDetail1);
order.addOrderDetail(orderDetail2);
testContainer.productRepository.save(product1);
testContainer.productRepository.save(product2);
testContainer.orderDetailCommendRepository.save(orderDetail1);
testContainer.orderDetailCommendRepository.save(orderDetail2);
testContainer.orderCommendRepository.save(order);
}
@AfterEach
void afterEach() {
BaseFakeOrderDetailRepository.getInstance().clear();
}
@Test
@DisplayName("์ทจ์/๋ฐํ ํ์ ")
public void confirmCancelReturn() {
// given
CancelRequestDto request = CancelRequestDto.builder()
.orderNo("1")
.cancelReason("๋จ์๋ณ์ฌ")
.build();
LocalDateTime updateTime = LocalDateTime.of(2024, 1, 31, 6, 7);
DateTimeHolder dateTimeHolder = TestDateTimeHolder.builder()
.updateTime(updateTime)
.build();
// when
CancelReturnResponseDto result = orderCancelReturnService.confirmCancelReturn(request, dateTimeHolder);
// then
assertThat(result.getOrderId()).isEqualTo(1L);
assertThat(result.getOrderNumber()).isEqualTo("1");
assertThat(result.getTotalPrice()).isEqualTo(8000L);
assertThat(result.getCancellationDate()).isEqualTo(updateTime);
assertThat(result.getRefundInfo())
.extracting("totalRefundAmount", "refundMethod", "refundProcessedAt")
.contains(8000L, "์นด๋", cancelDate);
assertThat(result.getCancelledItems()).hasSize(2)
.extracting("productId", "productNo", "name", "price", "quantity")
.contains(
tuple(1L, "1", "๋นต๋นต์ดํค๋ง", 1000L, 2L),
tuple(2L, "2", "์ฅ์ง์ํค๋ง", 2000L, 3L)
);
}
์ค์ ์ฌ์ฉํ ์๋น์ค ํ ์คํธ ์์์ด๋ค.
์์์ ๋ณด๋ฉด ์๊ฒ ์ง๋ง Repository๋ฅผ ์ปค๋งจ๋์ ์ฟผ๋ฆฌ๋ก ๋๋๋ ์ปจ๋งจ๋ ์ชฝ์์ ์ ์ฅํ๋๋ผ๋ ์ฟผ๋ฆฌ์ชฝ์์ ์ฐธ์กฐํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์ด ์๋ฌ๊ฐ ๊ณ์ ์๊ฒผ๋ค. ์ด ๋ฌธ์ ๋ ๋ค์ ๊ฐ๋ฐ์ผ์ง์ ํฌ์คํ ํด๋ณด๋๋ก ํ๊ฒ ๋น~!