교육/강의
[단위테스트] 테스트하기 어려운 영역 분리: 현재 시간 의존성 해결
youngyin
2025. 1. 22. 00:00
1. 테스트하기 어려운 영역: 시간 의존성
소프트웨어에서 현재 시간(LocalDateTime.now())에 의존하는 코드는 테스트하기 어렵습니다. 이는 테스트 실행 시점에 따라 결과가 달라질 수 있기 때문입니다. 예를 들어:
- 테스트가 영업 시간 내에 실행되면 성공하지만, 영업 시간 외에 실행되면 실패합니다.
- 동일한 테스트 코드가 실행 시간에 따라 불안정한 결과를 초래합니다.
2. 문제점
아래의 기존 코드는 현재 시간에 직접적으로 의존하고 있어 테스트 실행 시마다 결과가 달라질 가능성이 있습니다:
public Order createOrder() {
LocalDateTime now = LocalDateTime.now();
LocalTime currentTime = now.toLocalTime();
if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)){
throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
return new Order(LocalDateTime.now(), beverages);
}
문제점:
1. LocalDateTime.now() 호출 시점에 따라 결과가 달라질 수 있습니다.
2. 테스트 환경에서 현재 시간을 제어할 수 없어 영업 시간 내외의 동작을 모두 검증하기 어렵습니다.
3. 해결책: 외부에서 시간 주입
- 기본 동작에서는 현재 시간을 그대로 사용할 수 있도록 유지합니다.
- 테스트 시에는 외부에서 시간을 주입받아 다양한 시나리오를 검증할 수 있도록 메서드를 오버로딩합니다.
public Order createOrder() {
return createOrder(LocalDateTime.now());
}
// 테스트를 위해 외부에서 시각을 주입받는 오버로딩 메서드
public Order createOrder(LocalDateTime now) {
LocalTime currentTime = now.toLocalTime();
if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)){
throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요.");
}
return new Order(now, beverages);
}
4. 테스트 코드: 시간 의존성 분리 후 테스트
(1) 기본 동작 테스트
- 현재 시간을 기반으로 주문을 생성하는 기본 동작을 검증합니다.
@Test
@DisplayName("테스트를 실행하는 시간에 따라 실패할 수도 있는 테스트")
void createOrder() {
// Arrange
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
// Act
Order order = cafeKiosk.createOrder();
// Assert
assertThat(cafeKiosk.getBeverages()).hasSize(1); // 음료가 1개인지 확인
}
문제점:
- 위 테스트는 실행 시간이 영업 시간 내에 있을 때만 성공합니다.
- 시간이 외부에서 고정되지 않았기 때문에 불안정한 테스트입니다.
(2) 테스트 가능한 코드로 변경
- 외부에서 고정된 시간을 주입하여 영업 시간 내 주문을 테스트합니다.
@Test
@DisplayName("Happy Case: 영업 시간 내에 주문 생성")
void createOrderWithCurrentTime() {
// Arrange
CafeKiosk cafeKiosk = new CafeKiosk();
Americano americano = new Americano();
cafeKiosk.add(americano);
// Act
Order order = cafeKiosk.createOrder(LocalDateTime.of(2023, 1, 17, 14, 0)); // 고정된 시간 주입
// Assert
assertThat(cafeKiosk.getBeverages()).hasSize(1); // 음료가 1개인지 확인
}
5. 결론: 외부 주입을 활용해 테스트 가능한 설계를 하라
테스트하기 어려운 영역(시간, 외부 시스템 등)은 외부에서 주입받는 설계를 통해 해결할 수 있습니다.
- 외부에서 값을 주입받아 테스트 시나리오를 제어할 수 있습니다.
- 기본 동작을 유지하면서도 다양한 시나리오에 대한 테스트를 작성할 수 있습니다.