본문으로 건너뛰기
중급20

비결정적 요소 다루기: 시간, 랜덤, 외부 API 통제하기

현재 시각이나 랜덤 값에 의존하는 테스트는 실행할 때마다 결과가 달라질 수 있다. 이런 비결정적 요소를 통제해서 항상 같은 결과가 나오게 만드는 법을 익힌다.

  • #비결정적요소
  • #플래키테스트
  • #시간모킹

개념

비결정적(Non-deterministic)이란

같은 입력을 줘도 실행할 때마다 다른 결과가 나올 수 있는 코드를 비결정적이라 한다. 대표적인 원인 세 가지가 있다.

  1. 현재 시각new Date() 같은 값은 실행할 때마다 다르다
  2. 랜덤 값 — 난수 생성기는 매번 다른 값을 만든다
  3. 외부 API 응답 — 네트워크 상태, 외부 서비스 상태에 따라 응답이 달라지거나 실패할 수 있다

모듈 7(레슨 3)에서 배운 플래키 테스트(어떤 때는 통과, 어떤 때는 실패하는 테스트)의 상당수가 이 비결정적 요소를 통제하지 못해서 생긴다.

시간 통제하기

"12월에만 무료배송 이벤트가 적용된다"는 로직을 테스트한다고 하자.

function isFreeShippingEvent() {
  return new Date().getMonth() === 11  // 12월(0부터 시작하므로 11)
}

이 함수는 테스트를 실행하는 실제 날짜에 따라 결과가 달라진다 — 12월에 테스트를 돌리면 통과하지만, 다른 달에 돌리면 실패한다. 해결 방법은 현재 시각을 코드 안에 직접 박아두지 않고, 바깥에서 주입받게 만드는 것이다(모듈 9 레슨 9의 테스트 용이성과 정확히 같은 원리).

function isFreeShippingEvent(currentDate) {
  return currentDate.getMonth() === 11
}
 
// 테스트에서는 원하는 날짜를 직접 넣어서 검증
test("12월이면 무료배송 이벤트가 적용된다", () => {
  const december = new Date(2026, 11, 15)
  expect(isFreeShippingEvent(december)).toBe(true)
})

많은 테스트 프레임워크는 아예 시스템 시각 자체를 고정시키는 기능(예: jest.useFakeTimers())도 제공한다.

랜덤 값 통제하기

랜덤 값이 필요한 코드(예: 쿠폰 코드 생성)를 테스트할 때는, 난수 생성기에 **고정된 시드(seed)**를 줘서 항상 같은 "랜덤" 값이 나오게 만든다. 시드가 같으면 몇 번을 실행해도 같은 순서의 값이 나온다 — 겉보기엔 랜덤이지만 실은 예측 가능해지는 것이다.

외부 API 통제하기

레슨 3에서 배운 테스트 더블(스텁)을 써서, 실제 외부 서비스 대신 항상 같은 응답을 돌려주는 가짜로 바꿔 끼운다. 이렇게 하면 외부 서비스가 실제로 느려지거나 응답이 바뀌어도 우리 테스트 결과는 영향을 받지 않는다.

비결정성을 완전히 없앨 수 없는 경우

E2E(End-to-End) 테스트나 실제 운영 환경 점검처럼, 진짜 시간과 진짜 외부 서비스로 검증해야 하는 순간도 있다. 이럴 땐 비결정성을 없애기보다, 여유 있는 대기 시간, 재시도 로직으로 불안정성을 줄이는 접근을 쓴다 — 이는 스테이지 4(자동화)에서 더 자세히 다룬다.

실무에서 왜 필요한가

"어제는 통과했는데 오늘은 실패한다"는 리포트를 받았을 때, 그 원인이 진짜 코드 결함인지, 아니면 시간·랜덤·외부 API 같은 비결정적 요소 때문인지 구분할 수 있는 QA는 훨씬 빠르게 원인을 좁힐 수 있다. 이건 모듈 8(레슨 5)에서 배운 "인시던트가 곧 결함은 아니다"라는 원칙을 실제 코드 수준에서 적용하는 것이다.

실습 과제

과제 1 — 비결정적 요소 찾아내기 (10분)

다음 코드에서 비결정적 요소가 무엇인지 찾고, 어떻게 통제할 수 있을지 방향을 제안한다.

function generateOrderId() {
  return "ORD-" + Math.floor(Math.random() * 1000000)
}
 
function isWithinBusinessHours() {
  const hour = new Date().getHours()
  return hour >= 9 && hour < 18
}

과제 2 — 플래키 원인 진단하기 (10분)

"주말에만 실패하는 자동화 테스트가 있다"는 제보를 받았다. 비결정적 요소 관점에서 어떤 원인을 의심해볼 수 있을지 2가지 가설을 세운다.

자가 체크리스트

  • 비결정적 요소의 세 가지 대표 원인(시간·랜덤·외부 API)을 설명할 수 있다
  • 시간을 바깥에서 주입받게 만드는 방식이 왜 테스트를 안정적으로 만드는지 설명할 수 있다
  • 랜덤 값에 시드를 고정하는 이유를 설명할 수 있다
  • 플래키 테스트의 원인을 비결정적 요소 관점에서 진단할 수 있다

흔한 실수

  • 비결정적 요소가 원인인 실패를 곧바로 "가끔 발생하는 원래 그런 버그"로 치부하고 넘어간다. 시간·랜덤·외부 API 관점에서 원인을 구체적으로 좁힐 수 있는 경우가 많다.
  • 테스트 코드 안에서 직접 new Date()Math.random()을 호출하게 놔둔다. 외부에서 주입받는 구조로 바꾸지 않으면 통제가 불가능하다.
  • E2E 테스트에서도 모든 비결정성을 완전히 제거하려 애쓴다. 일부 상황은 재시도·여유 대기 시간으로 관리하는 게 더 현실적이다.

참고 자료