[단위테스트] 테스트하기 어려운 영역 분리: 현재 시간 의존성 해결

y001·2025년 1월 19일
post-thumbnail

1. 테스트하기 어려운 영역: 시간 의존성

소프트웨어에서 현재 시간(LocalDateTime.now())에 의존하는 코드는 테스트하기 어렵습니다. 이는 테스트 실행 시점에 따라 결과가 달라질 수 있기 때문입니다. 예를 들어:

  • 테스트가 주차장 운영 시간 내에 실행되면 성공하지만, 운영 시간 외에 실행되면 실패합니다.
  • 동일한 테스트 코드가 실행 시간에 따라 불안정한 결과를 초래합니다.

2. 문제점

아래의 기존 코드는 현재 시간에 직접적으로 의존하고 있어 테스트 실행 시마다 결과가 달라질 가능성이 있습니다:

public ParkingTicket issueTicket() {
    LocalDateTime now = LocalDateTime.now();
    LocalTime currentTime = now.toLocalTime();
    if (currentTime.isBefore(LOT_OPEN_TIME) || currentTime.isAfter(LOT_CLOSE_TIME)){
        throw new IllegalArgumentException("운영 시간이 아닙니다. 관리자에게 문의하세요.");
    }
    return new ParkingTicket(now, vehicle);
}

문제점:
1. LocalDateTime.now() 호출 시점에 따라 결과가 달라질 수 있습니다.
2. 테스트 환경에서 현재 시간을 제어할 수 없어 운영 시간 내외의 동작을 모두 검증하기 어렵습니다.


3. 해결책: 외부에서 시간 주입

  • 기본 동작에서는 현재 시간을 그대로 사용할 수 있도록 유지합니다.
  • 테스트 시에는 외부에서 시간을 주입받아 다양한 시나리오를 검증할 수 있도록 메서드를 오버로딩합니다.
public ParkingTicket issueTicket() {
    return issueTicket(LocalDateTime.now());
}

// 테스트를 위해 외부에서 시각을 주입받는 오버로딩 메서드
public ParkingTicket issueTicket(LocalDateTime now) {
    LocalTime currentTime = now.toLocalTime();
    if (currentTime.isBefore(LOT_OPEN_TIME) || currentTime.isAfter(LOT_CLOSE_TIME)){
        throw new IllegalArgumentException("운영 시간이 아닙니다. 관리자에게 문의하세요.");
    }
    return new ParkingTicket(now, vehicle);
}

4. 테스트 코드: 시간 의존성 분리 후 테스트

(1) 기본 동작 테스트

  • 현재 시간을 기반으로 주차 티켓을 발급하는 기본 동작을 검증합니다.
@Test
@DisplayName("테스트를 실행하는 시간에 따라 실패할 수도 있는 테스트")
void issueTicket() {
    // Arrange
    ParkingLotSystem parkingLotSystem = new ParkingLotSystem();
    Vehicle vehicle = new Vehicle("XYZ-5678");
    parkingLotSystem.parkVehicle(vehicle);

    // Act
    ParkingTicket ticket = parkingLotSystem.issueTicket();

    // Assert
    assertThat(ticket).isNotNull(); // 티켓이 정상적으로 발급되었는지 확인
}

문제점:

  • 위 테스트는 실행 시간이 운영 시간 내에 있을 때만 성공합니다.
  • 시간이 외부에서 고정되지 않았기 때문에 불안정한 테스트입니다.

(2) 테스트 가능한 코드로 변경

  • 외부에서 고정된 시간을 주입하여 운영 시간 내 발급을 테스트합니다.
@Test
@DisplayName("Happy Case: 운영 시간 내에 티켓 발급")
void issueTicketWithFixedTime() {
    // Arrange
    ParkingLotSystem parkingLotSystem = new ParkingLotSystem();
    Vehicle vehicle = new Vehicle("XYZ-5678");
    parkingLotSystem.parkVehicle(vehicle);

    // Act
    ParkingTicket ticket = parkingLotSystem.issueTicket(LocalDateTime.of(2023, 1, 17, 10, 0)); // 고정된 시간 주입

    // Assert
    assertThat(ticket).isNotNull(); // 티켓이 정상적으로 발급되었는지 확인
}

5. 결론: 외부 주입을 활용해 테스트 가능한 설계를 하라

테스트하기 어려운 영역(시간, 외부 시스템 등)은 외부에서 주입받는 설계를 통해 해결할 수 있습니다.

  • 외부에서 값을 주입받아 테스트 시나리오를 제어할 수 있습니다.
  • 기본 동작을 유지하면서도 다양한 시나리오에 대한 테스트를 작성할 수 있습니다.

📌 이 글은 TDD 강의를 학습한 내용을 바탕으로 재구성하였습니다. 문제가 되는 부분이 있다면 수정하겠습니다.

0개의 댓글