본문 바로가기

Java

JUnit 5와 Mockito로 테스트 코드 작성하기

 이번에는 자바 테스트 코드 작성 방법을 공부하면서 JUnit 5와 Mockito를 사용해 보았다. 처음에는 단순히 테스트를 실행하는 방법부터 시작했고, 이후 테스트 이름 지정, 반복 테스트, 조건부 테스트, 커스텀 태그, 확장 모델, 그리고 Mockito를 이용한 Mock 객체 테스트까지 실습했다.

 

JUnit5 이란 무엇일까???

Java 언어에서 독립된 단위 테스트(Unit Test)를 지원해 주는 프레임워크를 의미한다.

 


 JUnit 5에서는 @Test를 사용해서 테스트 메서드를 만들고, @BeforeEach, @AfterEach를 통해 각 테스트 실행 전후에 공통 작업을 처리할 수 있었다. 또한 @DisplayName을 사용하면 테스트 이름을 사람이 읽기 쉽게 표시할 수 있었다.

@TestFactory – 동적 테스트 지원
@DisplayName – 테스트 클래스, 테스트 메서드에 대해 사용자 정의하는 이름 노출
@Nested – 클래스가 중첩된 비정적 테스트 클래스임을 나타냄
@Tag – 테스트를 필터링하기 위한 태그 선언
@ExtendWith – custom extensions 등록
@BeforeEach – 각각의 테스트 메서드 실행 이전에 매번 실행 (JUnit4: @Before)
@AfterEach – 각각 테스트 메소드 실행 이후에 실행 (JUnit4: @After)
@BeforeAll – 현재 클래스의 모든 테스트 메서드 실행 이전에 한 번만 실행 (JUnit4: @BeforeClass)
@AfterAll –현재 클래스의 모든 테스트 메서드 실행 이후에 한 번만 실행 ( (JUnit4: @AfterClass)
@Disable – 테스트 클래스, 테스트 메서드 비활성화 (JUnit4: @Ignore)

 

 

 

  @DisplayName("스터디 만들기")
  @Test
  void createNewStudy() {
      Study study = new Study(10, "테스트");
      assertNotNull(study);
  }



@RepeatedTest와 @ParameterizedTest도 사용했다. 

@RepeatedTest는 같은 테스트를 여러 번 반복할 때 사용하고, @ParameterizedTest는 여러 값을 넣어가며 같은 테스트를 실행할 때 사용한다.

  @ParameterizedTest
  @CsvSource({"10, 자바 스터디", "20, 스프링"})
  void parameterizedTest(Integer limit, String name) {
      Study study = new Study(limit, name);
      System.out.println(study);
  }

 

  JUnit 확장 모델도 실습했다. BeforeTestExecutionCallback, AfterTestExecutionCallback을 구현해서 테스트 실행 시간을 측정하는 FindSlowTestExtension을 만들었다. 이를 통해 테스트 실행 전후에 내가 만든 로직을 끼워 넣을 수 있다는 것을 알게 되었다.

  Mockito에서는 Mock 객체와 Stubbing을 배웠다. Mock 객체는 진짜 객체 대신 사용하는 가짜 객체이고, Stubbing은 그 가짜 객체가 어떤 값을 반환하거나 예외를 던질지 미리 정하는 것이다.

given(memberService.findById(1L))
        .willReturn(Optional.of(member));


  이 코드는 memberService.findById(1L)이 호출되면 member를 반환하도록 정하는 코드이다.

  또한 void 메서드에 예외를 발생시키는 방법도 배웠다.

doThrow(new IllegalArgumentException())
         .when(memberService)
         .validate(1L);



  그리고 Mockito의 Verify 기능을 사용해서 특정 메서드가 실제로 호출되었는지도 확인했다.

  then(studyRepository).should(times(1)).save(study);



  마지막으로 StudyService의 openStudy() 메서드를 테스트했다. 이 메서드는 스터디를 공개 상태로 바꾸고, 저장소에 저장한 뒤, 알림을 보내는 역할을 한다.

 public Study openStudy(Study study) {
     study.open();
     Study openedStudy = repository.save(study);
     memberService.notify(openedStudy);
     return openedStudy;
 }


  이를 테스트하기 위해 studyRepository를 Mock 객체로 만들고, save()가 호출되면 같은 study를 반환하도록 Stubbing 했다.

  given(studyRepository.save(study)). willReturn(study);

  그다음 openStudy()를 실행하고, 상태가 OPENED로 바뀌었는지, 공개 시간이 저장되었는지, 알림 메서드가 호출되었는지 검증했다.

  @DisplayName("다른 사용자가 볼 수 있도록 스터디를 공개한다.")
  @Test
  void openStudy() {
      StudyService studyService = new StudyService(memberService, studyRepository);
      Study study = new Study(10, "더 자바, 테스트");

      given(studyRepository.save(study)).willReturn(study);

      studyService.openStudy(study);

      assertEquals(StudyStatus.OPENED, study.getStatus());
      assertNotNull(study.getOpenedDateTime());
      then(memberService).should().notify(study);
  }



 이번 실습을 통해 테스트 코드는 단순히 코드가 실행되는지 확인하는 것이 아니라, 객체들이 서로 어떻게 협력하는지 검증하는 도구라는 것을 알게 되었다. 특히 Mockito를 사용하면 DB나 외부 서비스 없이도 Service 로직만 독립적으로 테스트할 수 있다는 점이 중요했다.

 

정리
  JUnit 5는 테스트를 작성하고 실행하는 기본 도구이다. Mockito는 테스트에 필요한 의존 객체를 가짜로 만들어 원하는 상황을 연출할 수 있게 해 준다. Stubbing은 Mock 객체의 행동을 미리 정하는 것이고, Verify는 Mock 객체가 실제로 사용되었는지 확인하는 것이다.

이번 학습의 핵심은 다음과 같다.

 JUnit = 테스트 실행 도구
 Mockito = 가짜 객체를 만들어주는 도구
 Stubbing = 가짜 객체의 행동을 미리 정하는 것
 Verify = 가짜 객체가 호출됐는지 확인하는 것

비유하자면 StudyService는 일을 처리하는 직원이고, StudyRepository와 MemberService는 협력자이다. Mockito는 이 협력자들을 가짜로 만들어서, 직원이 일을 올바른 순서로 시키는지 확인할 수 있게 해 준다.