Mocking을 언제 어디서 사용해야 하는가?
실제로 테스트하기에 어렵거나 불필요한 경우 Mocking을 사용한다.
예시코드
@DisplayName("결제완료 주문들을 조회하여 매출 통계 메일을 전송한다.")
@Test
void sendOrderStatisticsMail() {
// given
LocalDateTime now = LocalDateTime.of(2023, 3, 5, 0, 0);
Product product1 = createProduct(HANDMADE, "001", 1000);
Product product2 = createProduct(HANDMADE, "002", 2000);
Product product3 = createProduct(HANDMADE, "003", 3000);
List<Product> products = List.of(product1, product2, product3);
productRepository.saveAll(products);
Order order1 = createPaymentCompletedOrder(LocalDateTime.of(2023, 3, 4, 23, 59, 59), products);
Order order2 = createPaymentCompletedOrder(now, products);
Order order3 = createPaymentCompletedOrder(LocalDateTime.of(2023, 3, 5, 23, 59, 59), products);
Order order4 = createPaymentCompletedOrder(LocalDateTime.of(2023, 3, 6, 0, 0), products);
// stubbing
when(mailSendClient.sendEmail(any(String.class), any(String.class), any(String.class), any(String.class)))
.thenReturn(true);
// when
boolean result = orderStatisticsService.sendOrderStatisticsMail(LocalDate.of(2023, 3, 5), "test@test.com");
// then
assertThat(result).isTrue();
List<MailSendHistory> histories = mailSendHistoryRepository.findAll();
assertThat(histories).hasSize(1)
.extracting("content")
.contains("총 매출 합계는 12000원입니다.");
}
메일을 보내는 서비스가 있는 경우 테스트마다 직접 메일을 보내야할까?
-> 리소스 소모가 크다.
when(mailSendClient.sendEmail(any(String.class), any(String.class), any(String.class), any(String.class)))
.thenReturn(true);
Stubbing을 이용하여 문제를 해결
둘다 가짜 객체이고 요청에 대한 리턴값을 가지고 있지만 검증하려는 목적이 다르다.
Stub 은 상태를 검증, Mock 행위를 검증한다.
Stub
class OrderStateTester...
public void testOrderSendsMailIfUnfilled() {
Order order = new Order(TALISKER, 51);
MailServiceStub mailer = new MailServiceStub();
order.setMailer(mailer);
order.fill(warehouse);
assertEquals(1, mailer.numberSent());
}
Mock
class OrderInteractionTester...
public void testOrderSendsMailIfUnfilled() {
Order order = new Order(TALISKER, 51);
Mock warehouse = mock(Warehouse.class);
Mock mailer = mock(MailService.class);
order.setMailer((MailService) mailer.proxy());
mailer.expects(once()).method("send");
warehouse.expects(once()).method("hasInventory")
.withAnyArguments()
.will(returnValue(false));
order.fill((Warehouse) warehouse.proxy());
}
}