백엔드 개발을 하며 테스트 코드를 작성하다 보면, 실제 데이터베이스나 외부 API와 같은 '의존성'을 그대로 사용하기 어려운 경우가 많습니다. 이때 등장하는 개념이 바로 테스트 더블(Test Double)입니다.
영화 촬영에서 위험한 장면을 대신 연기하는 스턴트 더블(Stunt Double)이 있듯이, 테스트 더블은 실제 의존성 객체를 대신하여 테스트에 활용되는 모든 객체를 통칭하는 용어입니다.
실제 의존성을 테스트에 그대로 포함시키면 다음과 같은 문제가 발생할 수 있습니다.
테스트 더블은 이러한 외부 요인으로부터 테스트를 격리시켜 빠르고, 안정적이며, 독립적인 테스트 환경을 만들어 줍니다.
테스트 더블은 그 역할과 복잡도에 따라 크게 5가지(Dummy, Stub, Fake, Spy, Mock)로 분류됩니다. (제라드 메스자로스의 분류 기준)

// 단순히 컴파일 에러를 피하기 위해 넘겨주는 껍데기
User dummyUser = new User();
boardService.createBoard(dummyUser, "제목");
// UserRepository의 스텁 구현
public class StubUserRepository implements UserRepository {
@Override
public User findById(Long id) {
// DB 조회 없이 무조건 미리 준비된 객체 반환
return new User(1L, "Alice");
}
}
HashMap이나 ArrayList를 사용하여 메모리 상에서만 데이터를 저장하고 조회하는 가짜 리포지토리(In-Memory Database).// 실제 DB 대신 Map을 사용하는 Fake 객체
public class FakeUserRepository implements UserRepository {
private Map<Long, User> data = new HashMap<>();
@Override
public void save(User user) {
data.put(user.getId(), user);
}
@Override
public User findById(Long id) {
return data.get(id);
}
}
// 호출 여부를 기록하는 Spy
public class SpyEmailService implements EmailService {
public int sendCount = 0; // 호출 횟수 기록
public String lastMessage = null;
@Override
public void send(String message) {
this.sendCount++;
this.lastMessage = message;
}
}
// Mockito를 활용한 Mock 예시
// 1. Mock 생성
EmailService mockEmailService = mock(EmailService.class);
// 2. 행위 수행
orderService.order();
// 3. 행위 검증 (verify): send()가 1번 호출되었는지 확인
verify(mockEmailService, times(1)).send(anyString());
테스트 더블을 이해할 때 가장 중요한 기준은 "무엇을 검증하는가?"입니다.
상태 검증 (State Verification):
save() 호출 후 findById()로 조회했을 때 데이터가 잘 들어있는가?행위 검증 (Behavior Verification):
order() 호출 시 emailService.send()가 정확히 1번 호출되었는가?Tip: 보통 실무에서는
Stub으로 상태를 세팅하고,Mock으로 행위를 검증하는 방식이 혼합되어 사용됩니다. 최근의 Mockito 같은 프레임워크는 Mock 객체 하나로 스텁(Stubbing)과 검증(Verifying)을 모두 처리할 수 있어 경계가 모호해지기도 했지만, 개념적으로는 분리해서 이해하는 것이 좋습니다.
테스트 더블은 외부 세계의 불확실성을 제거하고 오직 나의 로직(System Under Test)에만 집중할 수 있게 해주는 강력한 도구입니다.
각각의 특징을 잘 이해하고 상황에 맞는 적절한 테스트 더블을 선택한다면, 훨씬 더 견고하고 유지보수하기 쉬운 테스트 코드를 작성할 수 있습니다.