Development/Code

Java Mockito로 테스트 완성도 높이는 4가지 필수 팁

Danny Seo 2024. 8. 19. 14:42

목차

    Mockito 4가지 필수 팁

    Java Mockito는 자바에서 가장 인기 있는 테스트 라이브러리 중 하나로, 그만큼 사용이 쉽다는 점에서 많은 사랑을 받고 있습니다.

    오늘은 이 테스트 라이브러리의 설정에 대해 다루기보다는, 여러분의 테스트를 향상시킬 수 있는 몇 가지 팁을 제공하고자 합니다.

    아래에는 간단한 테스트 파일을 포함시켰으며, 이를 통해 몇 가지 유용한 테스트 케이스를 살펴볼 예정입니다.

    @ExtendWith(MockitoExtension.class)
    class MyTestClass {
    
      @Mock
      private ServiceToCall service; // 테스트 대상 서비스가 호출할 서비스
    
      @InjectMocks
      private ServiceUnderTest sut; // 테스트하려는 서비스
    
      @BeforeEach
      void setup(){
        //TODO 각 테스트 케이스 전에 실행될 설정을 구현하세요
      }
    
      @Test
      void testMyService(){
        //TODO 테스트 케이스를 구현하세요
      }
    
    }

     

    첫번째 팁: Mockito의 “when”을 정확히 사용하기

    겉보기에는 간단해 보이지만, 몇 가지 주의해야 할 점들이 있습니다. 가장 먼저, Mockito의 “when” 메서드는 테스트 대상 서비스(sut)가 동작을 수행하기 전에 사용해야 합니다. 때로는 코드가 아래와 같은 순서로 작성되는 것이 논리적으로 보일 수 있습니다:

    1. 서비스가 동작을 수행한다.
    2. “when” 문을 실행한다.
    3. 결과를 검증한다.

    하지만, 이 순서로 코드를 작성하면 원하는 결과를 얻기 어려울 수 있습니다.

    이 경우 “when” 문을 beforeEach 섹션에 포함시켜 각 테스트 실행 전에 호출되도록 설정할 수 있습니다.

    @BeforeEach
    void setup(){
        when(service.doSomething()).thenReturn("something");
    }

    이 예시에서는, “sut”가 서비스의 “doSomething” 메서드를 호출하면, 해당 서비스는 “something”을 반환하게 됩니다. 이 반환 값은 함수의 기대 결과와 일치해야 합니다.

     

    만약 테스트 케이스에서 “doSomething” 함수가 호출되지 않는다면, Mockito는 오류를 발생시킬 것입니다. 이는 테스트 케이스를 디버깅하는 데 매우 유용한 도구가 됩니다.

     

    때로는 특정 테스트 케이스에서만 필요한 “when” 문이 있을 수 있으며, 다른 테스트에서는 필요하지 않을 수도 있습니다. 이럴 때는 lenient.when을 사용할 수 있으며, 메서드가 호출되지 않더라도 오류가 발생하지 않습니다. 그러나 lenient에 의존하면 테스트 실패를 감지하지 못할 수 있으므로 주의가 필요합니다. 따라서, 테스트 케이스를 최대한 깔끔하게 유지하는 것이 중요합니다.

    다음 팁을 통해 이 목표를 달성할 수 있습니다.

    두번째 팁: 중첩 테스트 클래스를 활용하기

    중첩 클래스는 여러 클래스를 하나의 테스트 케이스 내에서 효율적으로 관리할 수 있는 좋은 방법입니다. 특히, 공통의 서비스나 변수를 공유할 때 유용합니다. 다음은 이러한 중첩 클래스를 테스트 케이스에 적용하는 예시입니다:

    @ExtendWith(MockitoExtension.class)
    class MyTestClass {
    
      @Mock
      private ServiceToCall service; // 테스트 대상 서비스가 호출할 서비스
    
      @InjectMocks
      private ServiceUnderTest sut; // 테스트하려는 서비스
    
      @Nested
      class ServiceDoSomething{
    
        @BeforeEach
        void setup(){
          when(service.doSomething()).thenReturn("something");    
        }
    
        @Test
        void testMyService(){
          //TODO 테스트 케이스를 구현하세요
        }
      }
    
      @Nested
      class ServiceDoesNothing{
    
        @BeforeEach
        void setup(){
          when(service.doSomething()).thenReturn(null);    
        }
    
        @Test
        void testMyService(){
          //TODO 테스트 케이스를 구현하세요
        }
      }
    }

    이 구현에서는 두 개의 중첩된 클래스가 있으며, 둘 다 service와 테스트 클래스 sut를 사용합니다. 하지만 각 클래스의 BeforeEach 설정이 다릅니다. 이 경우에는 덜 유용해 보일 수 있지만, 여러 테스트 케이스를 다룰 때 이 접근법은 매우 유용할 수 있습니다.

    세번째 팁: 읽기 쉽고 깔끔한 Assertion 작성하기

    개인적인 취향일 수 있지만, 저는 "org.assertj.core.api"의 assertThat 기능이 Assertion을 보다 가독성 있고 깔끔하게 만들어 준다고 생각합니다. 다음은 몇 가지 예시입니다.

    assertThat(someList).hasSize(1);
    assertNull(someText);
    assertThat(someList).doesNotContain("idk");
    assertThat(myText).isEqualTo("great");
    assertThat(someList).isNotEmpty();
    
    assertThat(someInfoList).isNotNull().hasSize(1)....

     

    여러 개의 Assertion을 다룰 때는 assertAll을 사용하는 것이 좋습니다. 이 방법은 여러 Assertion을 하나로 묶어 단일 테스트 케이스 내에서 여러 조건을 명확하고 체계적으로 확인할 수 있게 해줍니다.

    assertAll(
      () -> assertThat(text1).isEqualTo(text2),
      () -> assertThat(text1).isEqualTo(text3),
      ...
    );

     

    네번째 팁: 매개변수화된 테스트 사용하기

    때때로 여러 테스트 케이스를 각각 작성하는 것이 더 쉬워 보일 수 있습니다. 하지만 이럴 때는 입력의 한 가지 매개변수만 다르다면 매개변수화된 테스트를 사용하는 것이 좋습니다. 이와 관련해 이론보다 실용성을 중시하는 저는 JUnit 5를 사용한 예시를 보여드리겠습니다:

    @ParameterizedTest
    @ValueSource(ints = {1, 3, 5}) 
    void testNumbers(int number) {
      sut.doSomethingWithMyNumber(number);
    }

     

    또한, Enum과 CSV 형식의 데이터를 테스트 케이스에서 활용하는 것도 좋은 방법입니다. 다음 번 테스트 작성 시 이를 한번 시도해 보세요.

    이 글이 여러분의 테스트 작성 실력을 향상시키는 데 도움이 되었기를 바랍니다. 하지만 테스트가 아무리 깔끔하고 아름답게 작성되었더라도, 복잡한 코드에서 단 몇 개의 테스트 케이스만 작성되었다면 그 효과는 제한적일 수 있습니다.

     

    읽어주셔서 감사합니다! 이 글이 도움이 되셨다면 공감이나 댓글을 남겨주시면 감사하겠습니다. 여러분의 피드백은 큰 힘이 됩니다!

     

    읽어주셔서 감사합니다! 😊
    개발 관련 궁금증이나 고민이 있으신가요?
    아래 링크를 통해 저에게 바로 문의해 주세요! 쉽고 빠르게 도움 드리겠습니다.

    '개발자서동우' 프로필 보기