원문: 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. 코너 케이스1를 테스트하기 쉽지 않습니다. 시스템 클럭이 입력 조건을 제공하는 데에 좌우됩니다.
  2. 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)도 사용될 수 있습니다.


  1. 코너 케이스여러 매개변수나 조건들이 극단적인 값으로 조합되어 발생할 수 있는 문제를 말합니다. 이는 단순히 하나의 ‘가장자리’ 값이 아니라, 여러 ‘가장자리’ 값들이 동시에 충족되어 발생하는 복합적인 예외 상황입니다. 마치 직사각형의 모서리(corner)처럼, 여러 경계가 만나는 지점이라고 생각할 수 있습니다. ↩︎