
테스트 코드를 작성하다 보면 여러 가지 질문이 떠오릅니다. 그중에서도 특히 많은 고민을 불러일으키는 질문은 아래의 세 가지입니다:
코드에서 외부 요소(예: 현재 시간 LocalDateTime.now, 환경 변수, 외부 API 호출 등)를 직접 호출하면, 테스트 작성이 어려워지고 유지보수에 악영향을 줄 수 있습니다. 특히 외부 요소를 직접 호출하면 다음과 같은 문제가 발생합니다:
한 번 LocalDateTime.now와 같은 코드를 쓰기 시작하면, 프로젝트 전반에 퍼지면서 관리가 점점 어려워집니다. 따라서 의식적으로 외부 요소를 직접 포함한 로직을 지양하고, 외부에서 주입받아서 사용하도록 해야 합니다.
public ParkingTicket createTicket() {
LocalDateTime entryTime = LocalDateTime.now(); // 외부 요소 직접 호출
if (entryTime.toLocalTime().isBefore(OPEN_TIME) || entryTime.toLocalTime().isAfter(CLOSE_TIME)) {
throw new IllegalArgumentException("주차장이 운영되지 않는 시간입니다.");
}
return new ParkingTicket(entryTime, vehicle);
}
위 코드는 현재 시간에 따라 실행 결과가 달라질 수 있으며, 테스트하기도 어렵습니다.
public ParkingTicket createTicket(LocalDateTime entryTime) {
if (entryTime.toLocalTime().isBefore(OPEN_TIME) || entryTime.toLocalTime().isAfter(CLOSE_TIME)) {
throw new IllegalArgumentException("주차장이 운영되지 않는 시간입니다.");
}
return new ParkingTicket(entryTime, vehicle);
}
LocalDateTime.now()를 호출하는 대신, 현재 시간을 매개변수로 주입받아 사용합니다.@DisplayName("주차권 생성 시 입차 시간을 기록한다.")
@Test
void registeredEntryTime() {
// given
LocalDateTime fixedEntryTime = LocalDateTime.of(2025, 1, 1, 10, 0); // 고정된 값 사용
Vehicle vehicle = new Vehicle("123가 4567", VehicleType.SEDAN);
// when
ParkingTicket ticket = ParkingTicket.create(vehicle, fixedEntryTime);
// then
assertThat(ticket.getEntryTime()).isEqualTo(fixedEntryTime);
}
이렇게 고정된 입력값을 사용함으로써, 테스트가 언제 실행되든 동일한 결과를 얻을 수 있습니다. 또한 테스트를 읽는 사람은 고정된 입력값과 예상 결과를 보고 테스트 의도를 바로 이해할 수 있습니다.
Private 메서드는 객체 내부의 구현 세부사항입니다. 따라서 이를 직접 테스트할 필요는 없습니다. 그 이유는 다음과 같습니다:
Private 메서드를 테스트하고 싶다는 생각이 들면, 설계가 복잡하다는 신호일 수 있습니다. 이 경우 Private 메서드의 책임을 작은 객체로 분리하는 것을 고려해야 합니다.
테스트를 작성하다 보면, 테스트에서만 필요한 데이터 생성자가 필요할 때가 있습니다. 예를 들어, Request DTO를 생성하기 위해 테스트 전용 생성자를 추가할 수 있습니다. 이러한 생성자나 getter 같은 간단한 함수들은 프로덕션 코드에 추가되어도 괜찮습니다.
LocalDateTime.now와 같은 외부 요소는 최상위 레벨에서 주입받아야 한다.📌 이 글은 TDD 강의를 학습한 내용을 바탕으로 재구성하였습니다. 문제가 되는 부분이 있다면 수정하겠습니다.