Mock

qkdk·2024년 2월 5일
0

TDD

목록 보기
8/12

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을 이용하여 문제를 해결

TestDouble

  • 영화 촬영시 StuntDouble에서 유래된 단어로 테스트 하기 어려운 객체를 가짜로 만들어 테스트를 진행하는것

Dummy

  • 아무 것도 하지 않는 깡통 객체

Fake

  • 단순한 형태로 동일한 기능은 수행하나, 프로덕션에 쓰기에는 부족한 객체
    Ex) FakeRepository <- 진짜 DB가 아니라 메모리에 저장하는 Repository

Stub

  • 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체
  • 그 외에는 응답하지 않는다.

Spy

  • Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체
  • 일부는 실제 객체처럼 동작하고 일부만 Stubbing할 수 있따.

Mock

  • 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체

Stub VS Mock

둘다 가짜 객체이고 요청에 대한 리턴값을 가지고 있지만 검증하려는 목적이 다르다.
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());
  }
  • MailServiceStub의 보낸 숫자를 체크

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());
  }
}
  • warehouse의 동작을 체크

ETC

  • 트랜잭션을 타지않는 긴 작업이 있는경우 Service단에 @Transcation을 붙여주지 않는것이 좋다.
    • Transcation이 붙으면 모든 로직이 끝날때까지 커넥션을 잡고있기 때문
profile
qkdk

0개의 댓글

관련 채용 정보