블라디미르 코리코프 님의 "단위 테스트" 책을 정리한 포스팅입니다.
1. '단위 테스트'의 정의
- 작은 코드 조각을 단위로, 독립적으로 빠르게 검증하는 테스트
격리성
- 독립적으로 실행 가능한 상태 (실행 환경이나 다른 테스트에 영향받지 않음)
- 순서에 의존하지 않아야 함
의존성 종류 | 설명 | 문제점 | 해결 방법 |
공유 의존성 | 테스트 간에 공유되는 상태 ex) 정적 가변 필드, 싱글턴, 전역 변수 |
테스트 간 간섭 발생 | 공유 제거 각 테스트마다 초기화 |
비공개 의존성 | 코드에 드러나지 않는 숨은 의존성 ex) JDBC, LocalDateTime.now() 등 |
실패 원인 파악 어려움 | 프록시 추상화 (의존성 주입) |
프로세스 의존성 | 외부 프로세스에 의존하는 시스템 구성 요소 ex) RabbitMQ, 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 |