TotT: 시간은 무작위
원문: https://testing.googleblog.com/2008/04/tott-time-is-random.html (Translated by Google Gemini)
메서드의 입력값을 명확하게 식별할 수 없을 때 어떻게 제대로 테스트할 수 있을까요? 다음 Java 메서드를 살펴보세요.
/** 현재 시간으로부터 다음 분의 시작을 나타내는 Date 객체를 반환합니다. */
public Date nextMinuteFromNow() {
long nowAsMillis = System.currentTimeMillis();
Date then = new Date(nowAsMillis + 60000);
then.setSeconds(0);
then.setMilliseconds(0);
return then;
}
이 메서드를 효과적으로 테스트하는 데에는 두 가지 장벽이 있습니다.
- 코너 케이스1를 테스트하기 쉽지 않습니다. 시스템 클럭이 입력 조건을 제공하는 데에 좌우됩니다.
nextMinuteFromNow()
가 반환될 때, 시간이 변경됩니다. 이는 테스트가 단언이 아니라 추측이 될 것이며, 재현하기 어려운 저빈도 실패를 생성할 수 있습니다… 불안정성입니다! 예를 들어 클래스 로딩 및 가비지 컬렉션 일시 중지가 이에 영향을 미칠 수 있습니다.
System.currentTimeMillis()
가 무작위 숫자 제공자처럼 보이기 시작하나요? 맞습니다! 현재 시간은 *비결정성 (non-determinism)*의 또 다른 원천입니다. nextMinuteFromNow()
의 결과는 입력으로부터 쉽게 결정될 수 없습니다. 다행히도 이것은 쉽게 해결할 수 있습니다. 현재 시간을 제어할 수 있는 입력 매개변수로 만드세요.
public Date minuteAfter(Date now) {
Date then = new Date(now.getTime() + 60000);
then.setSeconds(0);
then.setMilliseconds(0);
return then;
}
// 원래 기능 유지
@Deprecated public Date nextMinuteFromNow() {
return minuteAfter(new Date(System.currentTimeMillis()));
}
minuteAfter()
에 대한 테스트를 작성하는 것은 nextMinuteFromNow()
에 대한 테스트를 작성하는 것보다 훨씬 쉬운 작업입니다.
public void testMinuteAfter () {
Date now = stringToDate("2012-12-22 11:59:59.999PM");
Date then = minuteAfter(now);
assertEquals("2012-12-23 12:00:00.000AM", dateToString(then));
}
이것은 이 문제를 해결하는 한 가지 방법일 뿐입니다. 의존성 주입(Dependency Injection)과 변경 가능한 싱글턴(mutable Singletons)도 사용될 수 있습니다.
-
코너 케이스는 여러 매개변수나 조건들이 극단적인 값으로 조합되어 발생할 수 있는 문제를 말합니다. 이는 단순히 하나의 ‘가장자리’ 값이 아니라, 여러 ‘가장자리’ 값들이 동시에 충족되어 발생하는 복합적인 예외 상황입니다. 마치 직사각형의 모서리(corner)처럼, 여러 경계가 만나는 지점이라고 생각할 수 있습니다. ↩︎