테스트 대상의 의존성을 가짜로 대체하는 객체: Mock, Stub, Spy
1. Mock
- 행동을 시뮬레이션하는 완전한 가짜 객체
- 내부 로직이 없으며 mock 객체의 메서드는 null/0/false 반환
- 사용 상황: 테스트 대상의 동작 여부(호출 여부, 횟수)를 검증하고 싶을 때
@Test
void service_shouldCallRepositorySave() {
UserRepository mockRepository = mock(UserRepository.class);
UserService service = new UserService(mockRepository);
service.registerUser("user");
verify(mockRepository).save(any(User.class)); // save가 호출됐는지만 검증
}
2. Stub
- mock 객체 중 특정 메서드 호출 시 미리 정의된 값을 리턴하도록 설정한 객체(Stub 역할의 mock 객체)
- 사용 상황: 의존 객체의 반환 값을 통제해서 테스트 대상의 조건 분기, 흐름을 검증하고 싶을 때 (ex. DB 조회 결과가 조건에 따라 달라지는 경우)
@Test
void service_shouldReturnTrueIfUserExists() {
UserRepository stubRepository = mock(UserRepository.class);
when(stubRepository.existsByName("user")).thenReturn(true);
UserService service = new UserService(stubRepository);
boolean exists = service.isUserExists("user");
assertTrue(exists);
}
3. Spy
- 실제 객체를 감싸서 일부만 mocking하는 객체
- 기본적으로 진짜 메서드가 호출되며, 특정 메서드만 가로채서 대체(stub)할 수 있음
- 사용 상황: 특정 메서드만 대체하여 내부 상태를 부분 검증할 때, 일부 사이드 이펙트를 방지하고 싶을 때
@Test
void spy_shouldCallRealMethodAndStubOne() {
List<String> realList = new ArrayList<>();
List<String> spyList = spy(realList); // realList를 감싼 객체
spyList.add("user1"); // realList.add("user1"); 호출
spyList.add("user2");
when(spyList.size()).thenReturn(100); // size 메서드만 stub
assertEquals("user1", spyList.get(0)); // 실제 메서드 호출
assertEquals(100, spyList.size()); // stub된 값 반환
}