테스트케이스 품질관리 기준
2026-09-09 사용자 결정. CASE-045 타이밍 실패 조사와 #1468 시간 제한 논의를 계기로, 기존 18개 검토 항목을 중복 없이 필수 조건 15개로 통합했다. 시작 조건과 재현성, 결함 탐지력과 공통 도구 검증, 정리 보장과 공통 준비·정리 예산을 각각 합쳤다.
이 문서는 권장 가이드가 아니라 개별 CASE의 품질 승인 기준이다. 해당 CASE에 적용되는 조건 하나라도 실패하거나 증거가 없으면 품질 승인을 내리지 않는다. 다른 항목의 통과 수, 높은 커버리지, 재실행 성공으로 상쇄하지 않는다.
정책 확정과 자동 강제 구현은 구분한다. 구현·실제 실행·전수 판정은 아래에 기록하며, 운영 반영과 증거 공백을 구분한다. 현재 코드의 강제 범위는 적용 상태에 별도로 기록한다.
적용 릴리스와 착수 조건
현재 적용 대상은 v0.17.9다. 2026-09-10 오너가 작업 중 v0.17.8의 배포 완료를 확인하고 #1478의 릴리스 이동을 승인했다. 이슈는 v0.17.9 milestone 8로 이동했고, release/v0.17.9는 당시 앱 main 6afe58ec66d063f7331bd27b360f39fb2d8fee72을 기준으로 생성했다. 작업 PR의 base는 release/v0.17.9다.
최초 배정·착수 이력: 2026-09-09에는 적용 대상을 v0.17.8로 정하고, v0.17.7의 실제 Production 배포 성공 커밋에서 분기하도록 승인했다. 2026-09-10 v0.17.7 Production Deploy 34375087828가 성공한 뒤, 배포 커밋 937042d775049b96ad00438fbe4019e1ec464e0c에서 release/v0.17.8과 codex/issue-1478-test-quality 작업 브랜치를 최초 분기했다. 이번 이동은 이미 수행한 분기 이력을 바꾸는 것이 아니라 현재 작업의 통합 목적지를 v0.17.9로 바꾸는 결정이다.
선행 #1468의 60초·10초·재시도 0 제약은 v0.17.7 배포 코드에 포함됐다. 현재 작업도 이 수치를 유지하며 남은 위반과 미검증을 보완한다.
현재 필수 대상은 앱 유저 여정 53 CASE + 화면 정합성 22 CASE = 75 CASE다. 2026-09-10 사용자가 와드업·관리자 CASE·단위·컴포넌트·DB 검사까지 모두 제외하도록 결정했다. 현재 범위와 제외 근거를 따른다.
구현과 로컬 검증을 마쳤다. 앱 93d4cad6의 현재 필수 75건은 15개 조건 모두 적합하다. 기존 79건 감사는 제외 전 이력으로 보존하며 CASE-056을 적합으로 바꾸지 않는다. Production 반영은 별도다.
필수 조건 15개
| 번호 | 필수 조건 | 승인에 필요한 증거와 불합격 조건 |
|---|---|---|
| 1 | 검증 목적과 범위가 명확하다 | CASE ID에 요구사항, 입력·행동, 기대 결과, 실제 검증 계층을 연결한다. 모의 응답으로 검사한 범위를 실제 서버·DB·외부 제공자 전체 검증으로 보고하면 불합격이다. |
| 2 | 시작 조건과 입력을 재현할 수 있다 | 필요한 계정·데이터·화면 상태를 준비하고 확인한다. 결과에 영향을 주는 시각·시간대·난수 seed·환경·버전을 통제하거나 기록한다. 벽시계를 쓰는 검사는 실행 시각과 상대 날짜 관계를 재현할 수 있어야 한다. 우연히 남은 데이터나 특정 연도가 영원히 과거라는 전제에 의존하면 불합격이다. |
| 3 | 독립적으로 실행된다 | 다른 CASE의 선행 실행 없이 단독 실행할 수 있고, 허용된 병렬 실행에서도 계정·DB 데이터·스토리지·포트·전역 상태가 충돌하지 않는다. 공용 자원을 변경했다면 원래 상태로 복원한다. 테스트 순서가 정답의 조건이면 불합격이다. |
| 4 | 상태와 사건으로 실행 순서를 제어한다 | 필요한 응답·화면 상태·저장 완료·탭 세대를 확인한 뒤 다음 행동을 한다. 임의의 고정 sleep이나 모든 네트워크가 조용해졌다는 사실만으로 준비 완료를 판단하면 불합격이다. 지연 자체를 입력으로 주입하는 검사는 주입과 해제 시점을 명시하고 실제 관측한다. |
| 5 | 정답을 독립적으로 판정한다 | 기대값은 요구사항·독립 계산·검증된 fixture 등에서 정한다. 검사 대상의 계산 함수를 다시 불러 기대값을 만들거나, 같은 오류를 공유하는 결과끼리 비교하는 것만으로 합격시키지 않는다. 화면·응답·DB를 비교할 때도 무엇을 정답으로 삼는지 명시한다. |
| 6 | 검사하려는 상황이 실제로 발생한다 | 충돌·오프라인·지연·응답 유실 등 선언한 조건이 해당 요청·탭·시점에서 발생했음을 확인한다. 실패 주입 코드를 등록했다는 사실만으로 충족되지 않는다. 주입이 실행되지 않았거나 다른 요청에 적용되면 불합격이다. |
| 7 | 관련 경계와 금지된 부작용을 검사한다 | 요구사항에 해당하는 빈 값·경계값·중복·권한·재진입 조건과 유실·중복 저장·다른 계정 변경 등 금지 결과를 단언한다. 모든 경계를 한 CASE에 넣을 필요는 없지만, 분리한 검사는 CASE ID로 연결한다. 관련 부작용을 확인하지 않고 성공 표시만 보면 불합격이다. |
| 8 | 필수 단언이 모두 실행되고 비동기 작업을 기다린다 | 필수 검증 지점을 식별하고 각 단언이 실제 성공한 뒤 완료 증거를 남긴다. 빈 반복문, 조건 분기로 건너뛴 단언, 조기 반환, 누락된 await, 처리되지 않은 오류가 있으면 불합격이다. 예상 오류는 요청·상태·횟수·발생 구간을 좁혀 선언하며 그 밖의 오류는 실패시킨다. |
| 9 | 목표 결함을 실제로 잡아낸다 | 정상 구현에서 통과하고, 목적에 대응하는 대표 결함을 의도적으로 넣으면 목표 단언에서 실패하는 증거가 있어야 한다. 문법 오류·준비 실패·관계없는 시간 초과는 결함 탐지 증거가 아니다. 공통 대기·요청 추적·실패 주입·정리 도구도 정상 완료, 취소·새로고침, 중도 실패의 관련 경로를 독립 검사한다. |
| 10 | 성공·실패 모두 자원을 끝까지 정리한다 | 계정·데이터뿐 아니라 테스트가 소유한 요청·타이머·리스너·탭·가로챈 응답을 완료 또는 취소하고 정리 결과를 확인한다. 새로고침 전 요청이 새 문서의 대기 목록에 남거나, 실제 보류 작업을 숨기기 위해 목록만 비우면 불합격이다. 공통 준비·사후 정리는 별도 유한 예산을 적용하고 실패도 결과에 반영한다. |
| 11 | CASE 실행은 60초 이내다 | Node·Playwright 개별 준비와 본문 실행의 합계 상한은 60초다. 개별 준비를 타이머 밖 hook으로 옮겨 우회하면 불합격이다. pgTAP SQL·공통 빌드·DB 기동·공유 준비·사후 정리의 별도 제한은 시간 정책을 따른다. |
| 12 | 동작·조건 대기는 1회 10초 이내다 | 클릭·입력 대상, 이동·새로고침, 화면 확인, 폰트·유한 애니메이션·배치 안정화의 대기 한도는 각각 최대 10초이며 CASE의 남은 60초 예산도 넘지 않는다. 하나의 조건 대기를 여러 번 새로 시작해 상한을 누적 연장하면 불합격이다. |
| 13 | 자동 CASE 재시도는 0회다 | 최초 시도의 결과로 판정한다. 실패한 CASE 전체를 자동으로 다시 시작하지 않으며 재시도 통과를 합격으로 인정하지 않는다. 한 시도 안에서 10초 내 같은 조건을 재조회하는 assertion polling은 허용한다. 원인 조사용 수동 재현은 별도 실행으로 기록하며 첫 실패를 덮어쓰지 않는다. |
| 14 | 실패를 숨기거나 기준을 약화하지 않는다 | 필수 CASE의 skip·only·조기 성공, 시간 초과 무시, timeout 상향, 오류 삼키기, 단언 삭제·약화, 필수 목록에서 몰래 제외하는 우회를 금지한다. CASE 분리는 원래 요구사항·단언·연결 시나리오와 각 CASE의 독립성을 보존해야 한다. 요구사항 자체 변경은 코드 변경과 같은 PR 검토에서 근거를 확인한다. |
| 15 | 첫 실패를 증거로 설명할 수 있다 | 실행 SHA·CASE ID·시도 번호·환경과 실패 단계, 기대값·실제값을 남긴다. 비동기 실패에는 관련 요청·탭·문서 세대·주입 시점·미완료 작업을 식별할 수 있어야 한다. 첫 실패를 정리 오류로 덮어쓰지 않고 두 오류를 구분한다. 민감한 토큰·사용자 원문을 진단 자료에 노출하지 않는다. |
번호 11~13은 #1468의 수치를 유지한다. 대기를 늘리거나 재시도로 우연히 통과시키는 방식으로 번호 4·9·10의 문제를 해결한 것으로 보고하지 않는다. 앱의 실제 저장·통신 timeout을 테스트 시간 제한과 혼동하지 않는다.
CASE별 합격 판정
각 CASE에는 실행 결과와 품질 기준 적합성을 따로 남긴다. 실행 한 번이 초록이어도 품질 증거가 부족하면 적합성은 미검증이다.
- 적합: 적용되는 15개 조건의 증거가 있고, 해당 검증 후보에서 필수 검사가 최초 시도에 완료됐다.
- 부적합: 조건 위반을 관측했다. 실패 원인과 해당 기준 번호를 기록한다.
- 미검증: 증거가 없거나 현재 검사기로 확인하지 못했다. 적합으로 간주하지 않는다.
CASE 실행 목록과 CASE별 증거는 앱 저장소의 실행 코드·기존 registry·구조화된 실행 산출물에서 연결한다. 최소 식별 정보는 CASE ID, 요구사항과 검증 범위, 필수 단언 식별자, 입력·환경, 주입 조건, 결함 탐지 증거, 정리 결과, 실행 SHA와 결과 위치다. 공유 helper의 검증 증거는 관련 CASE들이 같은 코드 버전의 증거를 참조할 수 있다. 동일한 자료를 CASE마다 복사하지 않는다.
관련 없는 주입 조건이나 경계를 억지로 만들지 않는다. 적용 범위는 요구사항과 테스트 계층을 근거로 실행 전에 정하고 기존 PR 검토에서 확인한다. 실행 중 자동으로 해당 없음을 붙이거나, 실패 후 필수 항목을 제외해 합격으로 바꾸는 것은 금지한다. 다른 CASE가 담당하는 요구사항은 그 CASE ID와 필수 실행 경로가 있어야 한다.
느슨한 지침으로 끝내지 않는 적용 명세
아래는 앱의 기존 정적 검사·fixture·결과 집계·필수 CI 검사에 적용할 명세다. 이 문서를 읽거나 체크박스를 채우는 것만으로 구현했다고 인정하지 않는다.
1. 코드 단계에서 우회를 차단한다
- 공통 runner와 등록 경로에서 60초·10초·재시도 0을 적용한다. 파일·CASE·CLI·환경변수의 override로 늘리거나 필수 검사를 빠뜨릴 수 있는지도 검증한다. 실제 시간 초과가 실패로 끝나고 정리가 진행되는 실행 검사를 둔다.
- 기존 registry·lint 검사에서 필수 CASE 목록, 중복 ID, skip·only, 누락된 await, 과도한 timeout, 실패를 성공으로 바꾸는 코드를 검사한다. 단순 문자열 유무 검사는 보조 수단으로만 사용한다.
- CASE별 필수 검증 지점을 기존 실행 manifest에 연결하고, 성공한 단언 이후에만 해당 지점의 완료 증거를 기록한다. 빈 callback 실행 횟수나
expect라는 문자열만으로 유효한 검증으로 인정하지 않는다. - 검사 설정·공통 도구를 바꾸는 PR도 같은 기준을 적용한다. 금지된 입력을 주었을 때 검사기가 실제로 거절하는 동작 검사로 우회 차단을 확인한다.
2. 실행 중 발생 사실과 정리를 확인한다
- 공통 fixture는 준비 확인 → 실제 행동·주입 관측 → 필수 단언 → 정리·재확인까지 완료되어야 CASE 완료 증거를 만든다. 본문만 반환되거나 시간 초과가 나면 정리를 시도하되 CASE는 실패다.
- 요청 추적은 CASE·탭·문서 세대와 연결한다. 정상 종료, 취소, 새로고침, 탭 닫힘에서 해당 작업의 실제 종료와 추적 종료를 맞춘다. 오래된 세대를 무시하는 것만으로 보류 응답의 정리 의무가 사라지지 않는다.
- 애플리케이션의 정상 주기 작업 전체가 영원히 0건이 될 것을 요구하지 않는다. 검증 목적에 필요한 작업과 테스트가 소유한 작업의 완료 조건을 구분해 선언하고 관측한다.
- 예상 실패는 선언된 대상·횟수·구간에만 허용한다. 예기치 않은 오류, 누락된 단언, 정리 실패는 본문 통과 여부와 무관하게 결과를 실패로 만든다.
3. 결과 집계에서 증거가 없으면 거절한다
- 해당 실행 경로의 필수 CASE 목록과 발견·실행·완료 목록을 대조한다. 필요한 CASE가 정확히 한 번, 같은 검증 후보 SHA·run의 최초 시도에서 통과했는지 검사한다. shard 누락·중복·skip·완료 증거 누락은 실패다.
- 필수 환경 설정이 없으면 실패시킨다. 실제 외부 제공자 인증처럼 사전 선언된 별도 조건부 경로는 그 경로의 범위와 실행 상태를 명시하며, 생략한 결과를 기본 경로의 검증 범위로 포함하지 않는다.
- 정적 검사·실행 검사·증거 집계 중 하나라도 실패하면 해당 단계의 필수
verify와 승격 경로가 거절해야 한다. 작업자가 결과 보고서만 작성해서 통과 상태를 대신할 수 없어야 한다. 아래 실행 시점의 구분은 품질 기준의 면제나 CASE 적합 판정의 생략이 아니다. - #1491의 승인 정책에 따라 작업 PR 전에는
npm run ci:precheck-local -- --release origin/release/v0.17.9로 최신 release를 반영하고 정적·단위·빌드 및 서버 변경 시 DB 검사를 수행한다. 이 사전검증에는 브라우저·viewport가 포함되지 않는다. - 작업→release는 기존
merge:request큐에서 최신 base·충돌·migration 순서와 위험·PR head/base·후보 tree·조건부 push의 병합 보호 검사를 거쳐 순차 통합한다. 일반 release 병합에서는 full CI를 실행하거나 full 성공 기록을 발급하지 않는다. release 병합 성공을 필수 CASE의 실행 성공 또는 15개 기준 적합으로 보고하지 않는다. - 최종 release→staging 병합 후보에서 hosted full CI와 증거 집계를 실행한다. staging→main은 동일 tree의 유효한 full 성공 기록과 현재 staging 배포·smoke 증거가 있을 때 재사용하고, 기록이 없거나 무효이면 full을 실행한다. 배포·smoke는 유지한다. 명시적
merge:request --dry-run의 진단용 full과ci:full-local -- --only <단계>의 부분 재현을 구분하며, 일반 작업 세션에서 전체 CI를 중복 실행하지 않는다.
4. 자동 판정이 어려운 의미는 구체적인 증거로 검토한다
번호 1·5·7·9의 요구사항 범위, 정답의 독립성, 적절한 경계, 대표 결함 선정은 일반 검사기 하나로 정확성을 보장할 수 없다. 기존 PR 검토에서 실제 단언과 결함 탐지 실행 결과를 확인한다. 별도 오너 승인 단계나 새로운 전역 큐를 추가하지 않는다.
신규 CASE 또는 목적·단언·공통 도구가 바뀐 CASE는 관련 대표 결함을 넣었을 때 목표 단언이 실패하는 증거를 갱신한다. 예를 들어 저장 중복 방지가 목적이라면 의도적으로 중복을 허용한 구현에서 중복 금지 단언이 실패해야 한다. 이 동작을 통과하지 못하면 단언 개수나 코드 커버리지가 높아도 승인하지 않는다. 관련 없는 모든 mutation을 매번 전체 실행하는 것은 요구하지 않는다.
모든 입력·운영체제 스케줄·미래 변경에 대해 테스트 자체가 무결함이라고 증명하는 기준은 아니다. 승인된 범위 안에서 통과의 근거와 실패 탐지력을 갖추고, 그 근거가 없을 때 합격을 거부하는 기준이다.
적용 상태
2026-09-10 변경 후보의 실제 근거다. 앱 코드는 93d4cad61d09d2c0c546145dd6dc73d8eab82721, 관리자 실행 코드는 37aa8f0cdc452e36346323eb0bce7ce3d1c77c83로 고정했다. 제외 전 79건 판정은 78/79 적합이었다. 사용자 결정 후 현재 필수 75건은 모두 적합이다. 변경 코드의 release 통합과 Production 배포는 별도로 확인한다.
| 구분 | 자동 강제와 실제 검증 범위 |
|---|---|
| 운영·통합 기준점 | 요청대로 실제 v0.17.7 Production 937042d7에서 분기했고, 작업 중 v0.17.8 배포 후 승인에 따라 v0.17.9로 옮겼다. 작업 PR의 base는 release/v0.17.9다. 이전 버전 배포를 이번 변경의 배포 성공으로 계산하지 않는다. |
| 시간·재시도 | #1468의 CASE 준비+본문60초, 동작·조건10초, 자동 CASE 재시도0을 유지한다. 공통 준비·정리는 별도 유한 예산을 적용한다. DB 통계 정리도 하나의10초 deadline 아래 실제 SQL을 취소하고 연결 종료를 확인한다. 로컬/hosted 필수 DB 명령에 실제 취소 회귀2개를 연결했다. |
| 필수 단언 | Playwright 실제 step/expect 시작·종료 사건과 polling에서 실제 성공한 단언으로 checkpoint 완료를 인정한다. 빈 callback·누락·중복·미완료 await·삼킨 실패·필수 목록 제외를 거절한다. viewport22건의 정확한117 checkpoint를 집계한다. |
| CASE별 대표 결함 | 각 CASE는 정상→실제 DOM/CSS 또는 지정 HTTP 응답 경계의 오답→같은 reader의 값 불일치→복원→정상값을 확인한다. 주입 횟수1, 목표값 불일치, 복원 완료를 요구하며 준비 실패·예외·timeout을 탐지로 인정하지 않는다. |
| 실제 서버 결함 | 소유자 분리038/044, 멱등 재실행041, 권한053/056, 원자성055/056에 총7개의 실제 SQL 함수 결함을 독립 임시 DB의 해당 사용자에만 주입했다. 원래 CASE 목표 단언의 최초 실패와 SQL 본문·소유자·권한 원복, 복원 뒤 정상 실행을 확인한다. 일반 응답 경계 control과 구분한다. |
| 요청·문서 수명 | Chromium CDP의 실제 문서/loader/요청과 종료 사건을 관측한다. 이전 문서의 미완료 요청을 삭제하거나 성공 종료로 바꾸지 않는다. 테스트 소유 보류 응답·route·page/context의 실제 종료와 리스너 제거를 확인한다. CASE014는 준비 조회 종료 후 문서를 바꾸며, 공통 완료는 실제 페이지 종료와 오류 확인 뒤 Auth를 삭제한다. |
| 성공·실패 정리 | 정상 및 본문/준비 실패 경로에서 소유 자원 정리를 수행한다. 첫 실패 증거를 먼저 남기고 정리 실패를 별도로 기록한다. 실제 Chromium 공통 회귀23개에는 새로고침·취소·보류 응답·중도 실패·리스너 소유권·페이지 종료/계정 삭제 경합이 포함된다. Auth/DB localhost 모형 회귀를 실제 DB 전체 검사로 확대하지 않는다. |
| 첫 실패·집계 | SHA·CASE·시도·환경·기대/실제값·실패 단계·관련 요청/문서·미완료 자원을 구조화한다. header/body를 수집하지 않고 민감값을 가린다. 실제 필수 계약과 checkpoint/control/cleanup의 식별자·결과를 대조한다. 첫 실행 실패 artifact를 후속 성공으로 덮어쓰지 않는다. |
| 앱·화면 실행 | 앱56+viewport22=78건, 실제 부분 실행 local-1788983964771-26036의 최초 시도 결과와 품질 집계를 사용했다. npm run check3,505pass/0fail/33조건부 DBskip, 공통 Chromium23/23. 필수 DB 검증을 포함한 precheck는 local-1788983833096-85404이며 pgTAP130파일/2,328단언과 실제 통계취소2개를 통과했다. 부분 실행을 full CI 성공으로 보고하지 않는다. |
| 관리자 CASE056 | 독립 관리자 check155/155. 2필수시나리오×정상/결함/복원3단계×원자성/권한2실험=12개 실행에서 목표 실패와 복원 정상, 정리 완료를 확인했다. 앱 소스 checkout 없이 고정 backend artifact와 독립 DB를 사용한다. 당시 hosted 실행은 secret 미등록으로 미검증이었다. 이후 사용자 결정으로 필수 대상에서 제외하고 수동 선택 실행으로 보존한다. 최신 결정은 관리자 단위·컴포넌트·DB 검사까지 자동 대상에서 제외한다. 기존 기준14의 U를 P로 변경하지 않는다. |
HTTP 응답 control은 실제 서버 요청의 응답 경계에서 오답을 관측할 수 있는지 검사한다. DB 제약 자체를 제거한 실험은 위 7개 SQL 결함에 한한다. UI/응답 경계 검사·실제 DB 검사·외부 제공자 성공을 가정한 인증 경계의 범위를 각 CASE에 명시하고, 검사하지 않은 전체 제품 결함이나 실제 외부 제공자 로그인을 검증했다고 보고하지 않는다.
기계가 강제하는 계약과 목적·정답·대표 결함의 적절성에 대한 검토를 함께 적용한다. 유한한 검증으로 미래의 모든 타이밍 조합에 대한 무결점을 수학적으로 보장한다고 주장하지 않는다. 해당 후보에서 조건 위반 또는 증거 공백이 있으면 개별 CASE 승인을 거절한다.
과거 확인 상태
2026-09-09 최초 확인에서는 앱 84e2d7d3553723d9b11a1a8de18efbc6e910af00과 PR #1469의 당시 Draft head 0da381aaaa6725ed054be546d9b8338df5c34e2f를 비교했다. 당시 본선의 일반 Playwright CASE 120초·expect 15초·CI 재시도 1회와 #1468 미병합 상태는 과거 기록이며, 위 v0.17.7 배포 이후의 현재 상태가 아니다.
당시 공통 fixture와 필수 결과 집계는 결과 신호·완료 절차·SHA/run/shard와 최초 시도 등을 검사했다. 이 실행 통과나 prove·expect 소스 패턴을 새로운 15개 기준의 적합성으로 소급 인정하지 않는다. 기존 감사 파일의 79×15 판정과 원래 실행 증거는 수정하지 않는다.
기준은 기존·신규 CASE 모두에 적용한다. 다만 기존 CASE의 실제 적합 상태는 증거를 확인한 뒤 표시하며, 기존 코드의 자동 차단 범위가 이미 이 문서 전체와 같다고 보고하지 않는다. 기준 번호는 후속 코드·문서 변경과 실행 증거를 연결하는 식별자로 유지한다.
관련 정책과 기술 근거
- 57+22건 품질 기준 적합성 점검 — #1478: 79건 × 15개 판정과 소스·기존 실행 증거.
- 테스트 시간 제한: #1468의 범위, 별도 준비·정리 예산, Node 실행상의 제한.
- E2E 마스터플랜: CASE와 필수 실행 경로의 전체 검증 범위.
- Playwright 테스트 작성 원칙: 사용자에게 보이는 동작·격리·조건 단언.
- Playwright assertion과 재시도: 한 시도 안의 조건 재조회와 CASE 재실행의 구분.
- Playwright clock: 시각·타이머를 통제하는 검사 방식.