블라디미르 코리코프 님의 "단위 테스트" 책을 정리한 포스팅입니다.
1. '단위 테스트'의 정의
- 작은 코드 조각 단위를 독립적으로 빠르게 검증하는 테스트
격리성
- 독립적으로 실행 가능한 상태
- 순서에 의존하지 않아야 함
의존성 종류 | 설명 | 문제점 | 해결 방법 |
공유 의존성 | 테스트 간에 공유되는 상태나 데이터 ex) 정적 가변 필드, 싱글턴, 전역 변수 |
테스트 간 상호 간섭 발생 | 공유 제거 테스트마다 초기화 |
비공개 의존성 | 코드 흐름상 드러나지 않는 의존성 ex) JDBC 연결, 시스템 시계 접근, 숨겨진 서비스 의존성 등 |
숨겨진 실패 원인 (원인을 파악하기 어려움) |
메모리 기반 프록시 |
프로세스 의존성 | 외부 프로세스에 의존하는 시스템 구성 요소 ex) 메시지 큐(RabbitMQ), 백그라운드 워커(Spring Batch 등) |
외부 시스템이 테스트에 영향 | Mock 객체 주입 |
예제) 공유 의존성
더보기
public class SharedService {
public static int sharedCount = 0;
public void increase() {
sharedCount++;
}
}
// 테스트 A
@Test
void testA() {
SharedService service = new SharedService();
service.increase();
assertEquals(1, SharedService.sharedCount); // 통과
}
// 테스트 B (순서나 병렬 실행에 따라 실패 가능)
@Test
void testB() {
assertEquals(0, SharedService.sharedCount); // 실패할 수 있음
}
- sharedCount는 정적 변수로, 여러 테스트에서 공유됨
- 테스트 간 간섭 발생
예제) 비공개 의존성
더보기
public class TimeService {
public boolean isBusinessHour() {
LocalTime now = LocalTime.now(); // 숨겨진 의존성
return now.isAfter(LocalTime.of(9, 0)) && now.isBefore(LocalTime.of(18, 0));
}
}
// 테스트
@Test
void testBusinessHour() {
TimeService service = new TimeService();
assertTrue(service.isBusinessHour()); // 테스트 환경의 시간에 따라 결과 달라짐
}
- LocalTime.now()는 숨겨진 외부 의존성
- 재현 어려움
예제) 프로세스 의존성
더보기
public class MessageSender {
private final RabbitMQClient client;
public MessageSender(RabbitMQClient client) {
this.client = client;
}
public boolean send(String message) {
return client.sendMessage(message); // 외부 시스템 의존
}
}
// 테스트
@Test
void testSendMessage() {
MessageSender sender = new MessageSender(new RabbitMQClient("real-host"));
assertTrue(sender.send("hello")); // RabbitMQ 가동 여부에 따라 실패
}
- 테스트가 실제 외부 메시지 큐 시스템에 의존
2. Mock
- 테스트 대역의 한 종류
- 테스트 중인 코드가 어떤 동작을 할 수 있도록 흉내만 내는 객체
- 실제 상태와 관계없이 테스트가 요구하는 방식으로 요청에 응답
항목 | 설명 |
🎯 목적 |
의존성으로 인해 테스트가 어려운 부분을 대체
|
📦 동작 방식 |
정해진 응답을 반환하거나, 특정 메서드 호출 여부를 검증
|
⚙️ 상태 무관 |
실제 동작이나 내부 상태에 의존하지 않음
|
🤝 인터페이스 필요 |
의존성을 추상화해서 주입 가능하도록 만들기 위해 보통 인터페이스 설계가 필요
|
예제) Mock - Repository
더보기
@Test
public void testAdd() {
// Mock 객체 생성
ResultRepository mockRepository = Mockito.mock(ResultRepository.class);
// 테스트 대상 클래스 생성 (Mock 주입)
CalculatorService service = new CalculatorService(mockRepository);
// 테스트 실행
int result = service.add(5, 3);
// 결과 검증
assertEquals(8, result);
// Mock 객체의 동작 검증
verify(mockRepository).saveResult(8); // saveResult(8)가 호출되었는지 확인
}
'Code > Test' 카테고리의 다른 글
[단위 테스트] 4-1. 좋은 단위 테스트의 4대 요소: 4대 요소 (0) | 2025.01.19 |
---|---|
[단위 테스트] 3. 단위 테스트 구조 (0) | 2025.01.07 |
[단위 테스트] 2-2. 단위 테스트란 무엇인가: 런던파와 고전파 (1) | 2025.01.07 |
[단위 테스트] 1-2. 단위 테스트의 목표: 커버리지 지표 (1) | 2025.01.06 |
[단위 테스트] 1-1. 단위 테스트의 목표: 단위 테스트 목표 (0) | 2025.01.06 |