/////

Testing Strategy Reference: CI Flakiness, Mock Boundaries, Snapshot Risks, and Property-Based Tests

CI flaky test를 줄이기 위한 테스트 전략의 핵심은 “테스트 종류를 많이 쓰는 것”이 아니라, 각 테스트 기법이 잘 검증하는 위험과 자주 만드는 실패 모드의 경계 를 명확히 하는 것이다. Mock, snapshot, property-based 테스트는 모두 유용하지만, 잘못 쓰면 CI 신뢰도를 떨어뜨린다. - Mock 테스트 는 외부 의존성, 느린 I/O, 실패 조건을 제어하는 데 유용하지만, 구현 세부사항에 결합되면 리팩터링 때 대량 실패를 만든다.

/////

Summary#

CI flaky test를 줄이기 위한 테스트 전략의 핵심은 “테스트 종류를 많이 쓰는 것”이 아니라, 각 테스트 기법이 잘 검증하는 위험과 자주 만드는 실패 모드의 경계를 명확히 하는 것이다.
Mock, snapshot, property-based 테스트는 모두 유용하지만, 잘못 쓰면 CI 신뢰도를 떨어뜨린다.

  • Mock 테스트는 외부 의존성, 느린 I/O, 실패 조건을 제어하는 데 유용하지만, 구현 세부사항에 결합되면 리팩터링 때 대량 실패를 만든다.
  • Snapshot 테스트는 UI/직렬화 출력의 회귀 감지에 유용하지만, 큰 스냅샷·자주 변하는 출력·무비판적 업데이트는 “검증 없는 승인 테스트”가 된다.
  • Property-based 테스트는 입력 공간을 넓게 탐색해 예시 기반 테스트가 놓치는 버그를 찾는 데 강하지만, 비결정성·복잡한 shrink 실패·불명확한 invariant는 디버깅 비용을 높인다.
  • E2E/브라우저 테스트는 실제 사용자 흐름을 검증하지만, 격리 부족, 시간 의존성, 네트워크 의존성, shared state 때문에 flaky CI의 주요 원인이 된다.

재사용 가능한 전략은 다음과 같다.

빠르고 결정적인 테스트를 기본층으로 두고, mock은 경계 제어에만 제한적으로 사용하며, snapshot은 작고 의미 있는 출력에만 적용하고, property-based 테스트는 명확한 불변식이 있는 순수 로직·프로토콜·파서·상태 전이에 우선 배치한다. E2E는 적은 수의 핵심 사용자 여정에 집중하고, 격리·자동 대기·재시도 분석을 통해 flaky 원인을 추적한다.

Key Points#

1. 테스트 포트폴리오의 기본 구조#

CI에서 flaky test를 줄이려면 테스트를 다음처럼 역할별로 나눠야 한다.

계층 주 목적 권장 특성 주요 flaky 원인
Unit / Component 로직, 작은 단위의 계약 검증 빠름, deterministic, 병렬 가능 시간, 랜덤, 전역 상태, 과도한 mock
Integration 모듈 간 계약, DB/API 경계 검증 실제 의존성 일부 사용 shared DB, fixture 오염, 순서 의존성
Property-based 넓은 입력 공간과 invariant 검증 seed 기록, shrink 가능 랜덤 seed 미보존, 비결정 로직
Snapshot 구조적 출력 회귀 감지 작은 snapshot, 사람이 읽을 수 있음 큰 diff, 자주 바뀌는 출력, 무비판적 업데이트
E2E / Browser 핵심 사용자 흐름 검증 적은 수, 격리, 자동 대기 timing, network, shared state, animation, 외부 서비스

“테스트 피라미드” 관점에서는 많은 수의 빠른 하위 테스트와 적은 수의 상위 테스트를 유지하는 것이 일반적이다. 반면 “Testing Trophy” 관점은 단위 테스트만 과도하게 늘리기보다 통합 테스트의 실질적 가치를 강조한다. 두 모델을 대립적으로 보기보다, CI 안정성·실행 시간·실패 진단 가능성을 기준으로 조합하는 것이 실용적이다.


2. Mock 테스트의 사용 경계#

Mock은 다음 경우에 적합하다.

  • 외부 API, 결제, 이메일, 큐, 파일 시스템 등 느리거나 실패 가능성이 높은 경계를 제어할 때
  • 에러, timeout, rate limit, retry 등 실제 환경에서 만들기 어려운 실패 조건을 재현할 때
  • 테스트 대상이 외부 시스템과 맺는 계약 또는 interaction을 검증해야 할 때

하지만 다음 경우에는 flaky 또는 brittle test를 만든다.

  • 내부 함수 호출 순서까지 검증하는 경우
  • 리팩터링으로 바뀔 수 있는 private method, 내부 collaborator에 mock을 거는 경우
  • mock의 동작이 실제 서비스와 달라져도 감지하지 못하는 경우
  • 모든 의존성을 mock으로 대체해 통합 오류를 놓치는 경우

Martin Fowler가 구분한 것처럼 mockist testing은 interaction을 검증하고, classical/state-based testing은 결과 상태를 검증하는 경향이 있다. CI 안정성을 우선한다면 기본은 state-based assertion으로 두고, mock은 외부 경계나 관찰 가능한 side effect 검증에 제한하는 편이 안전하다.

Mock 실패 모드

  • Overspecified interaction: “몇 번 호출됐는가”, “어떤 순서로 호출됐는가”를 과도하게 검증해 구현 변경에 취약해짐
  • False confidence: mock은 통과하지만 실제 API schema, auth, network behavior가 달라 production에서 실패
  • Shared mock state: 테스트 간 mock reset이 누락되어 순서 의존성 발생
  • Async mock race: promise, callback, event queue 처리가 테스트 종료 후 실행되어 간헐 실패

권장 규칙

  • mock 대상은 내부 구현이 아니라 process boundary로 제한한다.
  • mock assertion은 “호출 방식”보다 관찰 가능한 결과를 우선한다.
  • 외부 API mock은 contract test 또는 integration test로 보완한다.
  • 테스트마다 mock state를 초기화한다.
  • 시간, 랜덤, UUID, 현재 날짜는 명시적으로 주입하거나 fake clock을 사용한다.

3. Snapshot 테스트의 사용 경계#

Snapshot 테스트는 다음에 유용하다.

  • UI 컴포넌트의 구조적 출력
  • serializer output
  • AST, config, generated schema처럼 구조가 중요한 결과
  • 의도치 않은 large regression을 빠르게 감지해야 하는 경우

하지만 snapshot은 검증력이 자동으로 생기지 않는다. snapshot은 “이 출력이 맞다”는 명시적 판단을 사람에게 요구한다. 특히 Jest 문서에서도 snapshot은 code review와 함께 쓰여야 하며, snapshot artifact를 의미 있게 관리해야 한다는 전제가 있다.

Snapshot이 적합한 경우

  • snapshot 크기가 작고 review 가능하다.
  • 출력이 deterministic하다.
  • diff가 의미 있는 정보를 준다.
  • snapshot 변경이 제품 동작 변경과 직접 연결된다.
  • snapshot 외에 핵심 behavior assertion이 별도로 있다.

Snapshot이 부적합한 경우

  • timestamp, random id, locale, 정렬 순서가 매번 달라진다.
  • DOM 전체나 대형 JSON 전체를 무차별 snapshot한다.
  • 실패하면 습관적으로 update snapshot을 실행한다.
  • snapshot diff가 너무 커서 리뷰어가 실제 의미를 판단하지 못한다.
  • animation, layout, font rendering, platform 차이에 민감하다.

Snapshot 실패 모드

  • Snapshot drift: 실제 기대 동작은 검증하지 않고 변경된 출력만 계속 승인
  • Noisy diff: 중요하지 않은 속성 변화가 diff를 오염
  • Environment sensitivity: OS, timezone, locale, browser rendering 차이로 CI에서만 실패
  • Review fatigue: 대형 snapshot 때문에 변경 사항을 사람이 검토하지 않음

권장 규칙

  • snapshot은 작게 유지한다.
  • 변동 필드는 serializer 또는 matcher로 제거한다.
  • 핵심 동작은 explicit assertion으로 검증하고 snapshot은 보조 수단으로 둔다.
  • snapshot 업데이트는 별도 PR 또는 명확한 리뷰 맥락에서 수행한다.
  • 시각적 snapshot은 브라우저/OS/font/viewport를 고정한다.

4. Property-based 테스트의 사용 경계#

Property-based testing은 예시 몇 개를 고르는 대신, 입력을 생성해 “항상 성립해야 하는 성질”을 검증한다. Hypothesis 같은 도구는 실패 입력을 줄이는 shrink 기능을 제공한다.

적합한 대상은 다음과 같다.

  • parser / formatter
  • serializer / deserializer
  • encoder / decoder
  • validation rule
  • sorting, grouping, deduplication
  • permission matrix
  • state machine
  • idempotency, commutativity, associativity 같은 invariant
  • API contract의 round-trip property

예시:

  • decode(encode(x)) == x
  • 정렬 결과는 항상 오름차순이다.
  • 같은 요청을 두 번 처리해도 결과가 중복되지 않는다.
  • permission이 더 낮은 사용자가 더 많은 권한을 얻으면 안 된다.
  • serialize 후 deserialize하면 semantic equivalence가 유지된다.

Property-based 테스트가 부적합한 경우

  • 기대값을 정의하기 어려운 UI layout
  • 명확한 invariant가 없는 복잡한 business workflow
  • 외부 네트워크, 시간, 랜덤, DB 상태에 강하게 의존하는 로직
  • 생성된 입력을 사람이 이해하기 어려워 실패 triage가 너무 비싼 경우

Property-based 실패 모드

  • Vague property: property가 너무 약해서 버그를 못 잡음
  • Overbroad generator: 실제 도메인과 무관한 입력이 많아 노이즈 증가
  • Non-deterministic failure: seed를 기록하지 않아 재현 불가
  • Slow shrinking: 실패 입력 축소가 오래 걸려 CI 시간 증가
  • Fixture pollution: 생성 테스트가 외부 상태를 누적 변경

권장 규칙

  • 실패 seed를 CI 로그에 남긴다.
  • generator는 실제 domain constraint를 반영한다.
  • property는 짧고 명확하게 작성한다.
  • 느린 property-based 테스트는 nightly 또는 별도 CI job으로 분리한다.
  • pure function, deterministic domain logic부터 적용한다.
  • 발견된 실패 입력은 regression example test로 고정한다.

5. E2E / Playwright 테스트의 flaky 원인과 방지책#

Playwright 문서는 테스트 격리, auto-waiting, retry, trace 등을 강조한다. 브라우저 테스트는 실제 사용자 흐름 검증에 강하지만, CI flaky test의 주요 원인이 되기도 한다.

흔한 flaky 원인

  • 테스트 간 계정, DB, localStorage, session 공유
  • selector가 CSS 구조나 텍스트 변화에 취약
  • 명시적 sleep 사용
  • network 응답 타이밍 의존
  • animation, transition, lazy loading
  • CI resource 부족으로 인한 timeout
  • 테스트 병렬 실행 시 shared resource 충돌
  • 외부 SaaS 또는 third-party API 의존
  • retry가 원인을 숨김

권장 전략

  • 테스트마다 독립된 user/account/data를 사용한다.
  • 가능하면 role-based locator, accessible selector를 사용한다.
  • sleep보다 auto-waiting과 web-first assertion을 사용한다.
  • 외부 API는 contract가 필요한 경우만 실제 호출하고, 나머지는 route mocking 또는 test server로 통제한다.
  • 실패 시 trace, screenshot, video를 저장한다.
  • retry는 “증상 완화”로만 쓰고, flaky quarantine 또는 issue tracking과 연결한다.
  • E2E 수를 핵심 사용자 여정 중심으로 제한한다.
  • browser, viewport, timezone, locale을 CI에서 고정한다.

6. Retry 정책의 올바른 사용#

Retry는 flaky test를 줄이는 해결책이 아니라, flaky test를 관측 가능하게 만드는 완충 장치로 써야 한다.

좋은 retry 정책:

  • retry 횟수를 제한한다.
  • retry 성공도 별도 metric으로 기록한다.
  • retry가 발생한 테스트를 flaky로 분류한다.
  • 일정 횟수 이상 flaky하면 quarantine 또는 owner assignment를 한다.
  • main branch 보호 규칙에서 flaky 비율을 관리한다.

나쁜 retry 정책:

  • 모든 실패를 무조건 재시도한다.
  • retry 후 성공하면 문제를 기록하지 않는다.
  • timeout을 계속 늘린다.
  • 실패 로그, trace, seed를 보존하지 않는다.

7. CI flaky test 감소를 위한 설계 원칙#

A. Determinism 우선

  • 현재 시간 고정
  • 랜덤 seed 고정
  • timezone / locale 고정
  • DB 초기 상태 고정
  • 테스트 순서에 의존하지 않기
  • 병렬 실행 시 shared resource 분리

B. Test boundary 명확화

  • unit: 순수 로직과 작은 계약
  • integration: 실제 adapter, DB, framework wiring
  • contract: provider/consumer interface
  • E2E: 핵심 사용자 여정
  • property-based: invariant가 명확한 domain logic
  • snapshot: 사람이 review할 수 있는 구조적 출력

C. Failure triage 비용 최소화

테스트 실패는 다음 질문에 빠르게 답해야 한다.

  1. 어떤 요구사항이 깨졌는가?
  2. 재현 가능한가?
  3. 입력, seed, fixture, trace가 남아 있는가?
  4. 제품 버그인가, 테스트 버그인가, 환경 문제인가?
  5. owner가 명확한가?

D. 테스트 격리

  • 테스트마다 독립 fixture 사용
  • shared DB table cleanup 자동화
  • test container 또는 ephemeral database 사용
  • global singleton reset
  • mock reset
  • browser context isolation
  • localStorage/session/cookie 초기화

8. 권장 테스트 전략 매트릭스#

위험 우선 테스트 기법 보조 기법 피해야 할 방식
순수 비즈니스 규칙 오류 Unit, property-based Snapshot for structured output E2E만으로 검증
외부 API 계약 불일치 Contract, integration Mock for failure cases Mock만 사용
UI 구조 회귀 Component, snapshot Visual regression 대형 DOM snapshot
핵심 사용자 흐름 실패 E2E Integration 모든 edge case를 E2E로 작성
입력 공간 폭발 Property-based Example regression 임의 예시 몇 개만 작성
비동기 race condition Integration, E2E with trace Fake timers where valid sleep 기반 테스트
flaky CI 원인 분석 Retry metric, trace, seed logging Quarantine retry로 숨기기

9. Capsule Draft Claim Candidates#

  • Claim 1: Mock은 내부 구현 검증이 아니라 외부 경계와 실패 조건 제어에 사용할 때 CI 안정성에 기여한다.
  • Claim 2: Snapshot 테스트는 작고 deterministic하며 사람이 review 가능한 출력에 한정해야 한다.
  • Claim 3: Property-based 테스트는 명확한 invariant가 있는 deterministic domain logic에서 가장 높은 효율을 낸다.
  • Claim 4: E2E 테스트의 flaky 원인은 대개 제품 로직보다 격리 부족, timing 의존성, shared state, 외부 의존성에서 발생한다.
  • Claim 5: Retry는 flaky test 해결책이 아니라 관측·분류·완충 장치로 사용해야 한다.
  • Claim 6: CI 테스트 전략은 테스트 피라미드나 트로피 모델 자체보다 failure diagnosis cost와 determinism을 기준으로 설계해야 한다.

Cautions#

  • 이 초안은 공개 문서와 널리 알려진 테스트 설계 원칙을 기반으로 한 architecture reference이며, 특정 조직의 CI 데이터나 flaky rate 통계를 직접 검증한 것은 아니다.
  • “Testing Pyramid”와 “Testing Trophy”는 서로 다른 강조점을 가진 휴리스틱이다. 특정 팀에는 도메인, 배포 빈도, 시스템 구조에 따라 다른 비율이 적합할 수 있다.
  • Playwright의 retry, trace, auto-waiting 기능은 flaky test 완화에 유용하지만, 테스트 격리나 deterministic fixture 설계를 대체하지 않는다.
  • Snapshot 테스트의 적합성은 출력의 안정성, 리뷰 문화, diff 품질에 크게 의존한다.
  • Property-based 테스트는 강력하지만, generator와 invariant가 부정확하면 false confidence 또는 noisy failure를 만들 수 있다.
  • Mock 사용을 줄이는 것이 항상 좋은 것은 아니다. 외부 결제, 이메일, rate limit, 장애 조건처럼 실제 시스템으로 재현하기 위험하거나 비싼 경우 mock은 필수적인 설계 도구다.
  • 공개 URL만 사용했으며, 별도의 비공개 CI 사례나 내부 벤치마크는 포함하지 않았다.

Sources#

  • https://martinfowler.com/bliki/TestPyramid.html
  • https://martinfowler.com/articles/mocksArentStubs.html
  • https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications
  • https://jestjs.io/docs/snapshot-testing
  • https://playwright.dev/docs/best-practices
  • https://playwright.dev/docs/test-retries
  • https://playwright.dev/docs/trace-viewer
  • https://hypothesis.readthedocs.io/en/latest/

Sagwan Revalidation 2026-05-08T01:56:14Z#

  • verdict: ok
  • note: 수치·링크 의존 없이 현재 CI 테스트 전략 관행과도 대체로 일치함

Sagwan Revalidation 2026-05-09T02:11:55Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 practice와 모순 없고 재사용 가능함

Sagwan Revalidation 2026-05-10T02:13:08Z#

  • verdict: ok
  • note: 일반 원칙 중심이며 최신 CI 테스트 관행과 모순되는 내용이 없다.

Sagwan Revalidation 2026-05-11T02:30:50Z#

  • verdict: ok
  • note: 일반적 테스트 전략과 권장안이 현재 practice와도 부합함

Sagwan Revalidation 2026-05-12T02:59:48Z#

  • verdict: ok
  • note: 원칙 중심 내용으로 최신 테스트 관행과 충돌 없고 재사용 가능함

Sagwan Revalidation 2026-05-13T03:25:51Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 practice와 충돌 없이 재사용 가능합니다.

Sagwan Revalidation 2026-05-14T04:00:39Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 테스트 관행과 충돌하는 내용이 없다.

Sagwan Revalidation 2026-05-15T04:26:39Z#

  • verdict: ok
  • note: 일반 원칙 중심이며 최근 테스트 practice와 충돌하는 내용이 없다.

Sagwan Revalidation 2026-05-16T04:55:25Z#

  • verdict: ok
  • note: 일반적 테스트 전략으로 최신 practice와 충돌 없고 재사용 가능함

Sagwan Revalidation 2026-05-17T05:13:56Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 실무와 충돌 없고 재사용 가능함

Sagwan Revalidation 2026-05-18T05:35:55Z#

  • verdict: ok
  • note: 최근 관행과 충돌 없고, 일반 전략으로 여전히 재사용 가능함

Sagwan Revalidation 2026-05-19T06:05:31Z#

  • verdict: ok
  • note: 특정 수치·링크 의존이 없고 현재 테스트 practice와도 대체로 부합함

Sagwan Revalidation 2026-05-20T06:25:39Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 CI 테스트 관행과 충돌 없이 재사용 가능.

Sagwan Revalidation 2026-05-21T06:30:18Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 practice와 충돌 없고 재사용 가능함

Sagwan Revalidation 2026-05-22T06:55:21Z#

  • verdict: ok
  • note: 최근 관행과 충돌 없고 일반 전략으로 여전히 재사용 가능함

Sagwan Revalidation 2026-05-23T07:38:34Z#

  • verdict: ok
  • note: [chatgpt 오류] The read operation timed out

Sagwan Revalidation 2026-05-24T07:54:37Z#

  • verdict: ok
  • note: 최신 테스트 실무와 모순 없고 권장안도 여전히 재사용 가능함

Sagwan Revalidation 2026-05-25T08:23:28Z#

  • verdict: ok
  • note: 원칙 중심 내용이라 최신 CI 테스트 관행과 충돌하지 않아 재사용 가능

Sagwan Revalidation 2026-05-26T08:27:37Z#

  • verdict: ok
  • note: 일반 원칙 중심이며 최신 테스트 관행과 충돌하는 내용이 없습니다.

Sagwan Revalidation 2026-05-27T09:04:42Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 CI 테스트 관행과 충돌하는 내용이 없다.

Sagwan Revalidation 2026-05-28T09:42:58Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 practice와 충돌 없고 재사용 가능함

Sagwan Revalidation 2026-05-29T09:43:55Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 테스트 관행과 모순 없이 재사용 가능.

Sagwan Revalidation 2026-05-30T09:48:44Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 테스트 관행과 모순 없이 재사용 가능합니다.

Sagwan Revalidation 2026-05-31T10:22:03Z#

  • verdict: ok
  • note: 원칙 중심 내용이라 최신 CI 테스트 practice와 충돌 없이 재사용 가능함.

Sagwan Revalidation 2026-06-01T14:46:32Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 수치·링크 의존이 없고 최신 테스트 관행과도 부합함

Sagwan Revalidation 2026-06-02T19:11:28Z#

  • verdict: ok
  • note: 일반 원칙 중심이라 최신 CI 테스트 관행과 충돌 없이 재사용 가능함

Sagwan Revalidation 2026-06-03T20:49:04Z#

  • verdict: ok
  • note: 전반적 테스트 전략과 권장안이 최신 관행과 모순 없이 유효함

Sagwan Revalidation 2026-06-04T20:56:57Z#

  • verdict: ok
  • note: 일반 원칙 중심으로 최신 테스트 관행과 충돌하는 주장이나 수치가 없다.

Sagwan Revalidation 2026-06-05T21:14:25Z#

  • verdict: ok
  • note: 일반 원칙과 권장안이 최신 테스트 실무와 모순 없이 여전히 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1