ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] BDD 패턴의 테스트 코드 작성 시 Mockito의 어색함과 BDDMockito의 활용
    Backend/테스트 코드 2024. 10. 11. 13:44

     

    Mockito와 BDDMockito 둘 다 Java 기반의 테스트 프레임워크로, 모킹(mocking)을 지원한다. 이 둘은 기능상에 차이점은 없으나, 모킹하는 스타일 방식이 차이가 있다.

    1. Mockito를 통해 Mock 객체 생성하는 방법

    @Mock 애너테이션을 통해 Mock 객체를 생성하고, @InjectMocks 애너테이션을 통해 Mock 객체를 주입할 수 있다. 예를 들어, MailService 객체를 생성하기 위해서 MailSendClient와 MailSendHistoryRepository가 필요하다. 이를 위해 @Mock 애너테이션을 사용하여 MailSendClient와 MailSendHistoryRepository 클래스의 Mock 객체를 생성하고, @InjectMocks 애너테이션을 통해 이 Mock 객체들을 MailService에 주입할 수 있다.

    @ExtendWith(MockitoExtension.class)
    class MailServiceTest {
    
        @Mock
        private MailSendClient mailSendClient;
    
        @Mock
        private MailSendHistoryRepository mailSendHistoryRepository;
    
        @InjectMocks
        private MailService mailService;
    
    }

     

     

    2. 행위 주도 개발(BDD, Behavior-Driven Development)

    BDD란 행위 주도 개발이라고 불리며, TDD에서 착안한 방법론이다. TDD에서 발전된 개념으로, 사용자 요구사항에 맞춘 시스템의 행동에 초점을 맞춰 개발 및 테스트 과정을 진행한다.

    2-1. BDD의 특징

    • BDD는 사용자가 어떻게 사용할 것인가에 중점을 둔다. 즉, 시스템이 특정 입력을 받으면 어떤 행동을 해야 하는지를 작성한다.
    • Given-When-Then 패턴 : BDD에서는 테스트 시나리오는 Given-When-Then 구조로 작성한다.
      • Given : 어떤 초기 상태가 주어졌을 때 (테스트 전제 조건)
      • When : 특정 행동이 일어났을 때 (테스트 대상 동작)
      • Then : 기대하는 결과가 나와야 한다. ( 테스트 결과 검증)

    2-2. Given-When-Then 예시

    • given : sendEmail() 메서드가 호출되었을 때, 해당 메서드는 true를 반환하도록 설정한다.
      • anyString() : 어떤 문자열이 들어와도 상관없다.
    • when : sendMail() 메서드를 호출한다.
    • then : sendMail() 메서드가 호출되었을 때, 그 결과는 true여야 한다.
    @ExtendWith(MockitoExtension.class)
    class MailServiceTest {
    
        @Mock
        private MailSendClient mailSendClient;
    
        @Mock
        private MailSendHistoryRepository mailSendHistoryRepository;
    
        @InjectMocks
        private MailService mailService;
    
        @Test
        @DisplayName("메일 전송 테스트")
        void sendMail() {
            //given
            when(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString()))
                .thenReturn(true);
    
            //when
            boolean result = mailService.sendMail("", "", "", "");
    
            //then
            assertThat(result).isTrue();
            verify(mailSendHistoryRepository, times(1)).save(any(MailSendHistory.class));
        }
    
    }
    @RequiredArgsConstructor
    @Service
    public class MailService {
    
        private final MailSendClient mailSendClient;
        private final MailSendHistoryRepository mailSendHistoryRepository;
    
        public boolean sendMail(String fromEmail, String toEmail, String subject, String content) {
            boolean result = mailSendClient.sendEmail(fromEmail, toEmail, subject, content);
            if (result) {
                mailSendHistoryRepository.save(
                    MailSendHistory.create(fromEmail, toEmail, subject, content)
                );
                return true;
            }
            return false;
        }
    }

     

    3. Mockito를 사용하는 테스트 코드의 어색함

    테스트 코드를 작성할 때, BDD 형식에 맞추어 작성하기 위해 Given-When-Then 절을 사용하였다. 그러나 아래 코드를 보면 when() 메서드를 사용하여 테스트 전제 조건을 설정하고 있다. 전제 조건을 설정하는 것이므로 given 절에 위치하는 것이 맞지만, when() 메서드와 given 절 사이에서 어색함이 발생한다.

    Mockito의 when() 메서드는 TDD 패턴에 맞춰진 방식으로, BDD 스타일의 Given-When-Then 패턴과는 약간의 차이가 발생한다.

    @ExtendWith(MockitoExtension.class)
    class MailServiceTest {
    
        @Mock
        private MailSendClient mailSendClient;
    
        @Mock
        private MailSendHistoryRepository mailSendHistoryRepository;
    
        @InjectMocks
        private MailService mailService;
    
        @Test
        @DisplayName("메일 전송 테스트")
        void sendMail() {
            //given
            Mockito.when(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString()))
                .thenReturn(true);
    
            //when
            boolean result = mailService.sendMail("", "", "", "");
    
            //then
            assertThat(result).isTrue();
            verify(mailSendHistoryRepository, times(1)).save(any(MailSendHistory.class));
        }
    
    }

     

    4. BDDMockito를 사용하여 BDD 스타일에 맞춰 테스트 코드 작성하기

    BDDMockitoMockito를 상속받아 BDD 스타일의 테스트 작성을 지원한다. BDDMockito는 Mockito와 동일한 기능을 제공하면서도 Given-When-Then 패턴을 직관적으로 사용할 수 있도록 설계되었다. 따라서 Mockito 기능을 기반으로 BDD 패턴에 맞는 표현을 사용하여 더 직관적인 테스트 코드 작성을 돕는다.

    • Mockito : when().thenReturn()
    • BDDMockito : given().willReturn()
    @ExtendWith(MockitoExtension.class)
    class MailServiceTest {
    
        @Mock
        private MailSendClient mailSendClient;
    
        @Mock
        private MailSendHistoryRepository mailSendHistoryRepository;
    
        @InjectMocks
        private MailService mailService;
    
        @Test
        @DisplayName("메일 전송 테스트")
        void sendMail() {
            //given
            BDDMockito.given(
                    mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString()))
                .willReturn(true);
    
            //when
            boolean result = mailService.sendMail("", "", "", "");
    
            //then
            assertThat(result).isTrue();
            verify(mailSendHistoryRepository, times(1)).save(any(MailSendHistory.class));
        }
    
    }

     

    5. 결론

    Mockito와 BDDMockito 둘 다 모킹 기능을 제공하지만, BDD 패턴에 맞추어 테스트 코드를 작성할 때 Mockito를 사용하는 경우 약간의 어색함이 발생한다. 이는 when() 메서드를 Given 절에 사용하는 것이 BDD 흐름과 맞지 않기 때문이다.

    따라서 BDDMockito를 사용하여 Given-When-Then 구조에 맞춰 더 직관적이고 자연스러운 테스트 코드 작성이 가능하다.

Designed by Tistory.