Service TestCoverage

한솔·2025년 10월 23일

요약본

첫번째,
ServiceTest 클래스 상단에 모키토 선언,
안에 테스트 대상인 service에 @InjectMocks 선언,
테스트 시 주입될 클래스는 레포지토리이므로 @Mock 선언,

두번째,
이후에는 각 메서드 마다 단위테스트를 만드는데 이때,
(예를들어 create)
AAA패턴에 맞게 given, when, then.
given으로 입력될 데이터를 미리 대본처럼 만들고,
when으로 서비스의 메서드를 호출하고,
then의 assert/verify를 이용하여 결과값/메서드가 잘 반환/실행 되었는지 테스트한다.


핵심 개념

기본 세팅 핵심 개념 3개
1. Mock : 가짜 객체
2. Stubbing : 대본 써주는 것(이 메서드가 호출되면 이렇게 답해!)
3. Verify(검증) : 호출 자체를 확인(정말 그 메서드를 그 횟수만큼 불럿나?)

  • @ExtendWith(MockitoExtension.class) : 모키토 준비 완료!
    클래스 상단에 선언해줘야 한다.
  • @Mock : 가짜 객체 표시, ServiceTest 단에서는 Repository가 가짜로 만들어진다.
  • @InjectMocks : 진짜로 테스트할 대상, Service를 테스트할 예정이므로
@InjectMocks
BoardService boardService;

AAA패턴 : 테스트의 뼈대
1. Given(준비) : 입력 데이터 만들기
2. When(실행) : 서비스 메서드 호출
3. Then(검증) : 리턴값 검사(assert...) + 레포지토리 호출 검사(verify...)


내가 사용한 문법 정리

  1. 스텁(BDD 스타일)
given(mock.메서드(인자))).willReturn();
given(mock.메서드(인자))).willThrow(예외);
  1. 검증(호출 확인)
verify(mock, times(1)).메서드(인자);
verify(mock, never()).메서드(...);
verifyNoMoreInteractions(mock); <= 더 이상 건드리지 않았는지.
  1. 리턴 값 검사
assertNotNull(x);
assertEquals(a, b);
assertThrows(예외.class, ()-> 실행);
  1. 매처 (인자 아무거나/정확히)
any(Board.class)
anyString()
eq("고정값")
  1. void 메서드 예외 스텁(save 처럼 리턴 있으면, given...로 충분하지만 리턴 없는 void는
doThrow(new SomeEx()).when(mock).voidMethod(args);

내가 작성한 Create Test

Users users = mock(Users.class);
given(usersRepository.findByUsername("tester")).willReturn(Optional.of(users));
given(users.getUserId()).willReturn(10L);
  • Users는 DB 엔티티라 가짜로 만들었고
  • 서비스가 tester로 유저를 조회하면 있다고 응답
  • 그 유저의 id는 10L라고 대본을 줌
given(boardRepository.existsByTitle("제목")).willReturn(false);
  • 제목 중복 검사 결과: 중복 아님(false) -> 그래서 save까지 진행 가능
given(boardRepository.save(any(Board.class))).willReturn(
	new Board(1L, 10L, "제목", "내용", LocalDateTiem.now(), LocalDateTime.now())
);
  • 실제 저장은 안 하지만 save 부르면 이런 엔티티가 나온다라고 가짜 결과를 줌
BoardCreateDto result = boardService.create(boardCreateCommand, "tester");
  • 여기서만 진짜 로직이 실행 됨(서비스 메서드)
  • 내부에서 위의 스텁들을 쭉 호출하게 됨
assertNotNull(result);
assertEquals("제목", result.getTitle());
assertEquals("내용", result.getContent());
  • 리턴 DTO가 제대로 나왔는지 확인
verify(usersRepository, times(1)).findByUsername("tester");
verify(boardRepository, times(1)).existsByTitle("제목");
verify(boardRepository, times(1)).save(any(Board.class));
  • 서비스 흐름이 우리가 의도한 순서대로 레포지토리를 불렀는지 확인

여기까지가 완벽한 AAA 패턴이라고 볼 수 있다.

0개의 댓글