성장일지 8차

이태형·2024년 12월 27일

Growth Log

목록 보기
8/18

저희 회사는 테스트 코드를 짜지 않고 바로 개발을 진행하기 때문에 사실 코드가 많이 지저분하기도 하고.. 메서드가 너무 길어져서 다른 사람들 코드 보기가 너무 힘들어서 일단 나라도 깨끗하게 해보자 싶어서 클린코드 & TDD 기반 코드 작성을 위해 내용을 찾아보기 시작했슴다

JUnit 으로 단위테스트에 필요한 어노테이션, 각 역할들에 대해서 정리한 내용입니다.

Mock

  • 테스트 시 실제 구현 대신 사용되는 가짜 객체
  • Mock 객체는 원하는 방식으로 동작하도록 프로그래밍되어 있으며, 실제 의존성을 가지지 않고 테스트 수행 가능

Mock MVC(Mock Model-View-Controller)

💡
  • 서블릿 컨테이너를 테스트할 때, 실제 서블릿 컨테이너를 띄우지 않고 Mock 객체를 사용하여 테스트 수행 가능
  • Mock 객체가 서블릿을 모방(Mocking) 하기 때문
  • DispatcherServlet 을 Mocking 하고, 요청을 보내는 것처럼 테스트를 진행
  • Mocking 된 서블릿에 요청을 보낼 때는 Mock MVC 클라이언트를 사용해야 하며, Mock MVC 는 실제 HTTP 요청과 응답을 모방하는 Mock 객체로, 서블릿 컨테이너를 실행하지 않고도 컨트롤러의 동작을 테스트가 가능하게 함
  • Mock MVC 를 사용하여 테스트를 수행할 때, 테스트 메서드에서 Mock MVC 클라이언트를 생성하고 설정한 후, 원하는 URL 에 대한 요청을 보냄
  • Mock 객체를 통해 처리된 요청에 대한 결과를 얻을 수 있고, 테스트 결과를 검증할 수 있음
  • 이렇게 Mock 객체와 Mock MVC 를 사용하면 실제 서블릿 컨테이너를 실행하지 않고도 서블릿 관련 코드를 테스트 할 수 있어 테스트의 속도를 향상하고, 테스트 환경을 관리하기 쉽게 만들어줌

Mockito.mock()

  • 클래스 또는 인터페이스의 mock 객체 생성 가능
  • 또한 mock 객체를 사용해 해당 객체가 가진 메서드의 반환값을 조작하거나 해당 메서드가 호출되었는지 확인할 수 있음

@Test
public void UserRepositoryTest() {

		// UserRepository mocking하고 count메서드가실행되면 모킹한값(111L)을 리턴한다

    UserRepository localMockRepository = Mockito.mock(UserRepository.class);
    Mockito.when(localMockRepository.count()).thenReturn(111L);

    long userCount = localMockRepository.count();

    Assert.assertEquals(111L, userCount);
    Mockito.verify(localMockRepository).count();
}

@Mock

  • Mockito.mock() 의 축약어, 테스트 클래스에서만 사용 가능
  • 또한 Mockito annotation을 활성화해야만, @Mock 어노테이션 사용 가능
    • Junit5에서는@ExtendWith(MockitoExtension.class) 로 활성화
@ExtendWith(MockitoExtension.class)
public class MockAnnotationUnitTest {
    
    @Mock
    UserRepository mockRepository;
    
    @Test
    public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
        Mockito.when(mockRepository.count()).thenReturn(123L);

        long userCount = mockRepository.count();

        Assert.assertEquals(123L, userCount);
        Mockito.verify(mockRepository).count();
    }
}
  • 테스트 코드의 가독성을 높이고, 테스트가 실패했을 경우 실패 메시지에 세부 내용이 표시되므로 디버깅 용이
  • @InjectMocks 를 사용하게 되면, mocking 된 객체들을 해당 어노테이션이 설정된 객체에 주입해주어 mock 객체 주입과 관련된 설정 코드의 양을 줄일 수 있음

@MockBean

  • 애플리케이션 컨텍스트에 Mock 객체를 추가하기 위해 사용
  • mocking 된 객체는 스프링 애플리케이션 컨텍스트에서 동일한 유형을 가진 기존 빈 대체 가능
    • 동일한 유형의 빈이 정의되어 있지 않으면 새로운 Bean 이 추가됨
  • 외부 서비스와 같은 특정 빈을 모킹해야 하는 통합 테스트 작성시에 유용
@ExtendWith(SpringExtension.class) // JUnit5 
public class MockBeanTest {

    @MockBean
    AccountRepository accountRepository;

    @Autowired
    ApplicationContext context;

    @Test
    public void AccountCountTest() {
        Mockito.when(accountRepository.count()).thenReturn(123L);

        AccountRepository accountRepo = context.getBean(AccountRepository.class);
        long userCount = accountRepo.count();

        Assertions.assertEquals(123L, userCount);
        Mockito.verify(accountRepository).count();
    }
}
  • @MockBean 으로 등록을 하면 애플리케이션 컨텍스트에 mocking 된 객체가 등록되고 해당 필드에 주입됨

  • Controller 부분만 단위테스트를 진행하기 때문에 Controller 에서 Service 를 호출하는 부분을 가짜 행동으로 바꾸기 위해 모킹 된 동작을 수행하도록 설정


@MockBean
private LogManageService LogManageService;

@Test
public void testService() {
    Mockito.when(LogManageService.getAccessControlDetailLog(1))
            .thenReturn(MPAccessControlLog.DetailLog.builder().build());
}
  • @MockBean 으로 설정된 Service 를 When 을 이용해 특정 동작으로 대체해서 수행 가능

사용 이유

  1. 테스트 격리
    • 모든 의존성을 실제 구현체로 사용하는 것이 항상 옳은 방법이 아닐 수 있음
    • 특정 빈 ( 외부 시스템에 의존하는 서비스, 데이터베이스 접근 빈 등 ) 은 테스트 중에 실제로 실행되지 않도록 모킹하는 것이 필요
    • 외부 시스템이나 데이터베이스 상태에 영향을 받지 않고 독립적으로 수행 가능
  2. 성능 향상
    • 모든 빈을 실제로 초기화 하고 실행하는 것은 시간이 많이 걸릴 수 있음
    • 무거운 빈 ( 외부 서비스 통신, 복잡한 초기화 로직 포함 ) 인 경우 테스트 속도가 느려질 수 있어, 이러한 빈을 모킹하면 불필요한 초기화와 실행을 피할 수 있어 테스트 실행 속도가 빨라짐
  3. 특정 동작 제어
    • 특정 빈의 동작을 제어하고 테스트 하는 경우, 실제 빈의 동작을 사용하면 제어가 어려움
    • 예를 들어, 특정 메서드가 특정 값을 반환하도록 하거나, 예외를 발생시키도록 설정하고 싶은 경우 모킹을 통해 쉽게 제어 가능
  4. 의존성 문제 해결
    • 테스트 시 특정 의존성이 해결되지 않거나, 특정 환경에서만 존재하는 빈이 필요한 경우 모킹을 통해 이러한 문제를 해결 가능
    • 실제로 존재하지 않는 빈을 모킹하여 테스트 수행 가능

@MockBean 부작용

  • @MockBean 은 Spring 컨테이너 내에서 빈으로 등록되고 해당 빈이 실제로 호출되는 것처럼 동작
  • 애플리케이션 컨텍스트에 빈이 추가되는 방식으로 동작하여 테스트가 느려질 수 있음
  • Mokito 의 @Mock 은 완전히 격리된 환경에서 목 객체를 생성하고 동작을 정의하는 용도로 사용되어 애플리케이션 컨텍스트 자체가 존재하지 않고 테스트가 가능

0개의 댓글