← Posts

Testing · CI · Flaky

실무의 테스트 - 믿을 수 있는 테스트를 유지한다

테스트는 짜는 것보다 믿을 수 있게 유지하는 게 어렵다. 빠름·독립·결정적이라는 좋은 테스트의 성질, CI에서 자동으로 도는 이유, 무엇을 자동화하고 무엇은 사람이 하는지로 시리즈를 닫는다.

목차
  1. 좋은 테스트의 성질
  2. 빠르지 않으면 안 돌린다
  3. 독립적이지 않으면 서로 오염된다
  4. 결정적이지 않으면 신뢰를 잃는다
  5. CI에서 자동으로 돈다
  6. 무엇을 자동화하고 무엇은 사람이 하나
  7. 테스트가 못 지키는 자리
  8. 정리

테스트는 한 번 짜고 끝이 아니다. 수백 개가 쌓이면 믿을 수 있게 유지하는 것이 더 어려운 일이 된다. 그 실무를 보며 시리즈를 닫는다.

좋은 테스트의 성질

쌓인 테스트가 안전망 노릇을 하려면 몇 가지 성질을 갖춰야 한다.

  • 빠르다 - 자주 돌릴 수 있다.
  • 독립적이다 - 다른 테스트에 안 기댄다.
  • 결정적이다 - 같은 코드면 늘 같은 결과.
  • 명확하다 - 깨지면 무엇이 왜 틀렸는지 바로 읽힌다.

하나씩 왜 중요한지 보자. 이 성질이 무너지면 테스트는 안전망이 아니라 짐이 된다.

빠르지 않으면 안 돌린다

테스트 스위트가 몇십 분씩 걸리면, 사람은 매번 안 기다린다. “이따 한 번에” 미루다 결국 깨진 채로 커밋한다.

그래서 빠른 단위 테스트를 위주로 두고, 느린 E2E는 아껴 쓴다(피라미드가 그래서 아래가 넓다). 빠른 스위트는 저장할 때마다 돌려도 부담이 없어, 문제를 몇 초 안에 되짚어 준다. 느린 스위트는 안 돌려서 있으나 마나가 된다.

독립적이지 않으면 서로 오염된다

테스트가 순서에 기대거나 상태를 공유하면, A가 남긴 데이터 때문에 B가 통과하거나 실패한다. 그러면 하나만 돌릴 땐 되는데 전체로는 깨지는, 잡기 힘든 문제가 생긴다.

그래서 각 테스트는 자기 데이터를 만들고 끝나면 정리해, 어떤 순서로 돌려도 같아야 한다. 통합·E2E 글의 플래키 절반이 사실 이 독립성 문제였다.

결정적이지 않으면 신뢰를 잃는다

같은 코드인데 됐다 안 됐다 하면(플래키), 초록을 못 믿게 된다. 그러면 빨간 줄이 떠도 “또 그거겠지” 하고 무시하고 - 그 순간 테스트는 죽는다. 진짜 버그가 그 빨간 줄에 섞여 있어도 못 본다.

결정성을 깨는 건 대개 랜덤·현재 시각·외부 의존이다. 랜덤엔 고정 씨앗을, 시각엔 고정 시계를 주입해 통제한다(테스트 더블이 여기서도 쓰인다 - 시간·난수도 갈아끼운다). 플래키는 방치하지 말고 즉시 원인을 없앤다.

CI에서 자동으로 돈다

좋은 테스트도 안 돌리면 소용없다. 그래서 사람 손이 아니라 파이프라인이 돌린다 - 푸시할 때마다 CI가 자동으로 전체를 실행하고, 깨지면 배포를 막는다.

plaintext
푸시  →  CI가 테스트 실행  →  초록이면 배포 / 빨강이면 차단

이게 안전망을 강제한다. “돌리는 걸 깜빡했다”가 불가능해진다 - 통과 못 하면 아예 못 나간다. 늦게 잡을수록 비싼 버그를, 배포 문턱에서 자동으로 막는 것이다.

무엇을 자동화하고 무엇은 사람이 하나

모든 걸 자동화하려는 것도 강박이다. 자동과 수동은 잘하는 게 다르다.

  • 자동이 잘하는 것 - 반복되는 회귀 검사. 사람이 지루해 못 할 일을, 몇 초에 수백 번.
  • 사람이 잘하는 것 - 탐색적 테스트(“이렇게 하면 어떻게 되지?”), 사용성·디자인 감각처럼 정답이 애매한 판단.

자동 테스트는 아는 것이 안 깨졌나를 지키고, 사람은 모르던 문제를 찾는다. 둘은 대체가 아니라 분업이다.

테스트가 못 지키는 자리

테스트는 강력하지만 배포 전까지만 지킨다. 실제 트래픽·실제 데이터·실제 부하에서 무슨 일이 나는지는 테스트가 다 예측하지 못한다.

그 자리는 관측가능성이 맡는다 - 테스트가 배포 전을 지키고, 관측이 배포 후를 지킨다. 둘 다 “확신”을 다루지만 시점이 다르다. 그래서 테스트를 잘 짜도 관측은 따로 필요하다.

정리

  • 좋은 테스트는 빠르고·독립적이고·결정적이고·명확하다 - 이게 무너지면 안전망이 짐이 된다.
  • 느리면 안 돌리고, 안 독립적이면 서로 오염되고, 안 결정적이면 신뢰를 잃는다(플래키는 즉시 원인 제거).
  • CI가 푸시마다 자동으로 돌려 깨지면 배포를 막는다 - 안전망을 강제한다.
  • 자동은 아는 회귀를, 사람은 모르던 문제를 - 분업이다. 그리고 배포 후는 관측의 몫이다.

여기까지가 이 시리즈다. 왜 테스트하나(안전망·변경의 자유)에서 시작해, 어느 층에 얼마나(피라미드), 어떻게 격리하고(더블), 무엇을 노리고(대상), 진짜를 붙여 이음매를 보고(통합·E2E), 순서를 뒤집어 설계를 끌어내고(TDD), 마지막에 믿을 수 있게 유지하는 실무까지 왔다. 테스트의 목적은 버그를 없애는 게 아니라, 코드를 계속 자신 있게 바꿀 수 있게 하는 것이다.

관련 글