빙응의 공부 블로그

[Spring]단위 테스트 정리하기 Mockito (2/3) 본문

Spring/개인공부_이론

[Spring]단위 테스트 정리하기 Mockito (2/3)

빙응이 2024. 10. 18. 14:26

[Spring]단위 테스트 정리하기 JUnit(1/2) (tistory.com)

 

[Spring]단위 테스트 정리하기 JUnit(1/2)

단위 테스트에 대해 다 까먹어서 다시 복습하는 겸 작성해보자 📝JUnit5JUnit5는 자바 단위 테스트를 위한 테스트 프레임워크이다.여기에 더해 AssertJ도 함께 사용하여 스프링에서 편하게 테스트

quddnd.tistory.com

 

이번에는 Spring 기반의 웹 애플리케이션에서 테스트를 작성하는 법을 알아보자.

 

📝Mockito

Mockito??
개발자가 동작을 직접 제어할 수 있는 가짜 객체를 지원하는 테스트 프레임워크이다.

 

실제로 Spring으로 웹 애플리케이션을 개발하면, 여러 객체들 간의 의존성이 생겨 단위 테스트 작성이 어렵다.

이를 해결하기 위해 가짜 객체를 주입시켜 주는 Mockito를 사용할 수 있다.

 

✔stub

다른 객체 대신 가짜 객체를 주입하여 어떤 결과를 반환하라고 정해진 답변을 준비시키는 것을 Stub이라고 한다.

예를 들어 데이터베이스에 새로운 데이터를 추가하고 코드를 테스트한다고 하면, 가짜 데이터베이스를 주입하여 insert 처리 시에 반드시 1을 반환하도록 하는 것이다.

 

해당 코드처럼 조회시에 임의의 가짜 데이터를 명시할 수 있다.

when(boardRepository.findById(1L)).thenReturn(Optional.of(new Board("Title", "Content")));

✔Mocking??

모킹을 통해 테스트 중에 특정 메서드가 얼마나 호출되었는지, 어떤 인자로 호출되었는지 등을 검증할 수 있다. 이 과정에서 실제 객체의 동작을 무시하고 설정한 동작(예: 반환 값이나 예외 던지기)을 따른다.

// Mock 예제
verify(boardRepository, times(1)).findById(1L); // 해당 메서드가 한 번 호출되었는지 검증

📝Mockito 사용방법

1. Mock 객체 의존성 주입
  • Mockito는 가짜 객체의 의존성을 주입하기 위해 3가지 어노테이션을 사용한다.
    • @Mock : 가짜 객체로 만드는 어노테이션
    • @InjectMocks : 목 객체를 주입받아 테스트하려는 실제 객체를 의미 
    • @Spy : 실제 객체의 일부 메서드는 실제 동작을 하면서도 특정 메서드는 목 동작을 할때 사용
@RunWith(MockitoJUnitRunner.class)
public class BoardServiceTest {

    @Mock
    private BoardRepository boardRepository;

    @InjectMocks
    private BoardService boardService;

    @Spy
    private SomeOtherService someOtherService;

    // 테스트 메서드들...
}

 

Mockito 주요 검증 메서드

 

1. when() / thenReturn()

  • 설명 : 특정 메서드가 호출될 때의 동작을 설정한다. 목 객체가 특정 값을 반환하거나 예외를 던지도록 도와준다.
  • 예시
when(boardRepository.findById(1L)).thenReturn(Optional.of(new Board("Title", "Content")));

 

2. when() / thenThrow()

  • 설명 : 특정 메서드가 호출될 때 예외를 던지도록 설정한다.
  • 예시
when(boardRepository.findById(-1L)).thenThrow(new RuntimeException("Invalid ID"));

3. doReturn() / doThrow()

  • 설명: when()과 유사하지만, void 메서드나 실제 객체(특히 @Spy)를 테스트할 때 유용하다. when()은 void 메서드에서 사용할 수 없기 때문에, doReturn()과 doThrow()를 사용한다.
  • 예시
doReturn(Optional.of(new Board("Title", "Content"))).when(boardRepository).findById(1L);
doThrow(new RuntimeException()).when(boardRepository).deleteById(-1L);

4. verify()

  • 설명: 메서드가 특정 횟수만큼 호출되었는지, 호출 순서가 맞는지 등 메서드 호출을 검증한다.
  • 예시
verify(boardRepository, times(1)).save(any(Board.class));
verify(boardRepository, never()).deleteById(-1L);

5. times(), never(), atLeast(), atMost()

  • 설명: verify()와 함께 사용되어 메서드 호출 횟수를 명시적으로 설정할 수 있다.
  • 예시
verify(boardRepository, times(2)).findAll(); // 2번 호출
verify(boardRepository, atLeast(1)).save(any(Board.class)); // 최소 1번 호출
verify(boardRepository, atMost(3)).deleteById(anyLong()); // 최대 3번 호출
verify(boardRepository, never()).findById(-1L); // 호출되지 않음

6. any(), eq(), argThat()

  • 설명: 메서드 인자를 매칭할 때 사용한다. 인자의 타입이나 특정 조건을 설정할 수 있다.
  • 예시
when(boardRepository.findById(any(Long.class))).thenReturn(Optional.of(new Board("Title", "Content")));
when(boardRepository.save(argThat(board -> board.getTitle().equals("New Title")))).thenReturn(new Board("New Title", "Content"));

7. doAnswer()

  • 설명: 메서드 호출 시 커스텀 동작을 정의할 수 있도록 합니다. 복잡한 동작이 필요한 경우 사용합니다.
  • 예시
doAnswer(invocation -> {
    Long id = invocation.getArgument(0);
    return new Board("Dynamic Title", "Dynamic Content with ID: " + id);
}).when(boardRepository).findById(anyLong());

8. reset()

  • 설명: 목 객체의 동작 설정을 초기화합니다. 재사용되는 테스트 클래스에서 각 테스트마다 초기화가 필요할 때 사용합니다.
  • 예시
reset(boardRepository);

9. doNothing()

  • 설명: void 메서드의 동작을 지정하지 않고, 호출 시 아무 작업도 하지 않도록 설정합니다.
  • 예시
doNothing().when(boardRepository).deleteById(anyLong());

10. spy()

  • 설명: 실제 객체의 동작을 유지하면서 특정 메서드만 모킹할 때 사용합니다.
  • 예시
BoardService spyBoardService = spy(new BoardService(boardRepository));
when(spyBoardService.getAllBoards()).thenReturn(new ArrayList<>());