불안정한 테스트(Flaky Test)를 축소된 예제 해시로 키링하기
요약
본 문서는 불안정한 테스트(Flaky Test)를 관리하고 'freeze'하는 새로운 게이트(Gate) 메커니즘을 제안합니다. 이 시스템은 단순한 테스트 이름 기반의 동결이 아닌, diff 분석, 해시된 증인 축소, 그리고 외부 재실행 검증 등 엄격한 조건을 통해 패치의 자격을 결정합니다. 이를 통해 실제 운영 환경에서의 실패 가능성을 줄이고, 더 신뢰할 수 있는 테스트 스위트 관리를 목표로 합니다.
핵심 포인트
- 불안정한 테스트의 'freeze'는 단순 면제가 아닌, 3가지 조건 충족이 필요함.
- 패치 자격은 diff 분석, 해시된 증인 축소, 외부 재실행 검증을 통해 결정됨.
- 제안된 게이트는 모델 점수 매기기가 아니며, 정확성을 보장하지 않음.
- 테스트의 신뢰성 확보를 위해 시드 목록 체크인 및 접두사 실행이 권장됨.
불안정한 테스트가 멈추는 것(flaky-test freeze)은 통과한 결과가 아니라 일종의 면제입니다. 에이전트 패치(agent patch)의 경우, 그 면제를 기록하려면 다음 세 가지 조건이 충족되어야 합니다. 첫째, diff가 fixture factories와 property strategies를 건드리지 않았어야 하고, 둘째, 실패한 속성(property)이 해시된 증인(hashed witness)으로 축소되었으며, 셋째, 이 증인이 에이전트 작업 공간 외부에서 재실행되어야 합니다. 단순히 테스트 이름만으로 저장된 freeze는 기준을 충족하지 못합니다.
에이전트 패치는 종종 입력값을 그리는 생성기(generator)를 수정하거나, 속성(property)을 좁혀서 실패하는 사례가 다시 나타나지 않도록 함으로써 빨간색이었던 속성을 초록색으로 만듭니다. 테스트 스위트는 깨끗해 보입니다. 하지만 실제 운영 환경에서의 실패는 여전히 도달 가능합니다. 이후의 freeze는 오래된 테스트 이름을 가리키며 재정의된 입력값을 숨깁니다.
이 메모는 측정 연구가 아니라 제안된 게이트(gate)입니다. 아래 명령어와 코드 스니펫은 실행되지 않은 예시입니다. 사용자의 lockfile에 의존성 버전을 명시하십시오. 경로 목록을 모든 저장소의 완전한 지도처럼 취급하지 마십시오.
이 게이트가 결정할 수 있는 것들
이 게이트는 하나의 질문에 답합니다: 이 패치가 짧은 flake freeze를 받을 자격이 있는가?
이는 정확성을 결정하지 않습니다. 모델을 점수 매기지 않습니다. 검토(review)를 대체하지도 않습니다.
| Diff 클래스 | 축소 증인 (Shrink witness) | 깨끗한 재실행 (Clean replay) | Freeze 결정 |
|---|---|---|---|
| 운영 경로만 (Production paths only) | 존재하며, 해시가 저장된 예제와 일치함 | 사전에 선언된 횟수 하에서 결과가 반전되며, 생성기 해시가 기본값과 일치함 | 짧은 freeze를 받을 자격이 있음 |
| ... |
위에서 아래로 행을 읽으십시오. 나중한 열은 이전의 거부(deny) 결정을 덮어쓰지 않습니다.
해당 함수는 경로 정책(path policy) 전용입니다. 어설션(assertions)을 파싱하지 않으며, 테스트 함수 내부에 존재하는 제너레이터(generator)를 잘못 분류할 수 있습니다. 이러한 누락 때문에 모든 test_body 결과는 별도의 라인 레벨 오라클 디프(line-level oracle diff) 검토가 이루어질 때까지 동결(freeze)을 거부합니다. 만약 그 두 번째 디프를 실행할 수 없다면, '거부' 상태를 유지하십시오.
부분 문자열 일치(Substring matches)는 임베드된 트리(vendored trees)에서 거짓 양성(false-positive)일 수 있습니다. 이 스케치(sketch)를 채택한다면, 자신만의 레이아웃에서 앵커링된 접두사(anchored prefixes)를 사용하는 것이 좋습니다. 동일한 경로 목록은 동일한 클래스를 산출해야 합니다. 코드로 분류기(classifier)를 확인하고, 정책 파일이 누락되면 작업을 실패시키십시오. 누락된 맵은 통과가 아닙니다.
단계 2. 체크인된 시드 목록에서 속성 가져오기
매번 작업마다 새로운 무작위 추출(random draw)을 하는 것은 면제(waiver)를 지원할 수 없습니다. 당신이 동결시키는 것은 입력값이 아니라 이름일 것입니다. 시드 목록을 체크인하고, 그 접두사(prefix)를 실행하십시오.
# 제안된 시드 계획. 미실행.
import json
from pathlib import Path
...
32는 측정된 최적값이 아니라 예산 조절기(budget knob)입니다. 속성이 저렴할 때 높이고, 설정이 무거울 때 낮추십시오. 나중에 읽는 사람이 어떤 시드가 적격했는지 볼 수 있도록 선택한 접두사 길이를 증인(witness)에 작성하십시오.
가설 검정 라이브러리(Hypothesis) 또는 동등한 라이브러리는 해당 시드를 명시적으로 받아야 합니다. 러너(runner)가 시드를 수락할 수 없다면, 그 속성에 대한 동결 경로를 열지 마십시오. 예제 테스트는 여전히 다른 방식으로 잠글 수 있습니다. 그것들은 이 게이트 밖에 있습니다.
단계 3. 실패 사례 축소 후 증인 해시하기
실패 시에는 저장하기 전에 축소(shrink)하십시오. 증인은 속성 ID, 시드 예산, 여전히 실패하는 가장 작은 예제, 그리고 병합 기준점(merge base)의 제너레이터 파일 해시를 담고 있습니다.
# 제안된 증인 작성기. 미실행.
import hashlib
import json
...
8 KiB 수치는 제한 상한선(cap)을 보여주는 예시일 뿐, 조정된 한계는 아닙니다. 어떤 상한선을 강제하십시오. 상한선이 없는 증인은 작업 공간의 덤프가 됩니다. 키 순서가 변경되면 예제를 변경하지 않고도 해시를 변경할 수 있으므로, 정규화된 JSON(Canonical JSON)이 중요합니다.
freeze key는 (property_id, example_sha256, generator_hashes)입니다. 이는 테스트 함수 이름이 아닙니다. 테스트 이름을 변경하더라도 면제(waiver)를 유지해서는 안 됩니다. 생성기(generator)를 편집하는 경우에도 마찬가지로 면제를 유지할 수 없습니다. 왜냐하면 해당 해시들이 바뀌기 때문입니다.
만약 축소(shrinking)가 단계 제한(step cap)에 도달한다면, 아무것도 저장하지 않고 거부해야 합니다. 불완전한 축소는 부분적인 통과를 의미하지 않습니다.
4단계. 에이전트 작업 공간 외부에서 증인(witness) 재실행하기
깨끗한 트리(clean tree)에서 재실행합니다. 에이전트 작업 공간에는 편집된 무시 파일, 따뜻한 캐시(warm cache), 또는 부분적으로 적용된 형식 통과(format pass)가 포함될 수 있습니다. 이 중 어느 것도 면제 결정에 속해서는 안 됩니다.
# 제안하는 재실행 스케치. 실행되지 않음. 이것을 더러운 에이전트 작업 트리(dirty agent worktree)에 가리키지 마십시오.
git worktree add --detach /tmp/gate-replay HEAD
# 해당 작업 트리에 증인 파일만 배치한 다음, 한 속성(property)을 실행합니다...
깨끗하게 실행된 결과를 좁게 해석해야 합니다. 안정적인 통과는 이전 실패가 오염 때문이거나, 분류기(classifier)가 이미 거부했어야 할 생성기 편집이었음을 의미합니다. 동일한 해시에서 안정적으로 실패하는 것은 실제 반례(counterexample)입니다. 제품 코드(product code)를 수정하거나 패치(patch)를 거부해야 합니다. 이를 동결(freeze)해서는 안 됩니다.
동결은 해당 증인이 정책에 기록된 카운트 동안 통과와 실패 사이에서 플립(flip)하는 경우, 그리고 생성기 해시가 기본값과 여전히 일치할 때만 적격합니다. 이 초안은 플립률이 없다고 명시합니다. 큐(queue)가 감당할 수 있는 카운트를 선택하고, 결과를 기록하며, 카운트가 불완전할 때는 실패로 처리해야 합니다.
리뷰 노트는 이 형태를 사용할 수 있습니다. 아래 값들은 포획된 실행 결과가 아닌 자리 표시자입니다.
{
공개: 이 문서는 MonkeyCode의 제품 홍보 활동의 일환으로 작성되었습니다. 게이트는 모델이 필요하지 않습니다. 초안 작성이 별도의 경로입니다. 이 워크플로우에 대한 운영자 제공 가용성은 두 가지 사실로 제한됩니다. 즉, MonkeyCode가 무료 모델 액세스를 제공하고, 무료 서버 옵션을 제공한다는 것입니다.
본 문서는 모델 이름, 할당량, 하드웨어, 기간 또는 영구성에 대해 단언하지 않습니다. 주어진 날짜에 둘 중 하나의 옵션이 누락되면, 증인이 없는 동결은 거부됩니다.
무료 모델 액세스는 프로덕션 diff를 초안 작성하는 데만 사용하십시오. 축소된 증인을 실패한 예제로 전달하고, 생성기 경로는 초안 지침에서 제외하십시오. 초안 작성이 완료된 후, 분류기를 다시 실행하십시오. `conftest.py` 또는 시드 파일에 접근하는 초안은 동결되지 않고 폐기됩니다.
재생(replay)이 패치 빌드와 같은 머신을 공유해서는 안 될 때 무료 서버를 클린 재생 호스트로 사용하십시오. 이 서버는 오라클이 아니라 작업 공간 경계입니다. 작업을 수락할 수 없다면, 동결 설정을 해제하십시오. 바쁜 큐가 패치를 승격시킬 이유는 아닙니다.
이미 초안 작성을 입학(admission)과 분리하는 독자들은 이 두 옵션으로 그 분리를 시도한 다음, 분류 및 해싱을 로컬로 유지할 수 있습니다. 제품이 호출되지 않는다면 속성 검사는 유효합니다.
## 제한 사항
경로 규칙은 테스트 함수 내부에 작성된 생성기를 놓칩니다. 이것이 기본적으로 테스트 파일 편집이 동결을 거부하는 이유입니다. 축소(Shrink)가 완료되지 않을 수 있으므로, 러너는 단계 제한(step cap)이 필요하며 불완전한 축소를 거부로 처리해야 합니다. 클린 호스트는 여전히 에이전트 작업과 DNS, 시계 또는 컨테이너 이미지를 공유할 수 있으므로 환경적인 결함은 이동을 생존할 수 있습니다.
해시 동등성은 속성이 의미가 있음을 증명하지 않습니다. 항진명제(tautology)는 사소한 예제로 축소되고 여전히 해싱됩니다. 여기서는 거짓 승인율(false-accept rate)이 보고되지 않습니다. 이 표는 정책입니다. 비율이 필요한 팀은 자체적인 변이 샘플을 실행하고 방법을 게시해야 합니다. 다른 게이트에서 비율을 가져오지 마십시오.
## 누가 이것을 건너뛰어야 하는가
스위트가 속성(properties)이 없고 스냅샷 테스트만 있는 경우 게이트를 건너뜁니다. 스냅샷은 이 키가 사용할 축소 단계(shrink step)가 없습니다. 실패가 물리적 장치나 직렬화 가능한 입력이 없는 타사 UI에 의존하는 경우 건너뜁니다. 인증, 암호화 또는 마이그레이션을 건드리는 패치에 대해서는 건너뜁니다.
해당 차이점(diffs)들은 면제할 것이 아니라 검토가 필요합니다. freeze store가 테스트 이름으로만 키를 생성할 수 있는 경우 게이트를 건너뛸 수 있습니다. 이름만 사용하는 스토어는 이 정책이 해결하는 숨기는 문제(hiding problem)를 재현합니다. 또한 시드 파일이 검토되지 않은 경우에도 건너뜁니다. `tests/seeds/agent_gate.json`을 다시 작성하는 에이전트는 diff에 팩토리 경로가 나타나지 않더라도 변경을 일으킬 수 있습니다. 테이블을 신뢰하기 전에 해당 파일을 생성기 클래스(generator class) 안에 넣으십시오.
## 검토 완료 (Closing check)
면제(waiver)를 작성하기 전에, 검토 노트에는 네 가지 사실이 필요합니다: diff 클래스가 프로덕션 전용인지, 목격자 해시(witness hash)가 존재하는지, 생성기 해시가 기본값과 일치하는지, 그리고 깨끗한 재실행(clean replay)이 선언된 횟수보다 적게 뒤집혔는지 여부입니다. 사실 중 하나라도 누락되면 freeze는 닫힙니다. 패치는 여전히 수정될 수 있습니다. 테스트 이름으로는 면제될 수 없습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기