Testing
문제
프로덕션을 건드릴 수 있는 CLI를 테스트해야 하는데 격리했다고 믿을 근거가 없습니다. 임계값은 감으로 정했고, 커밋할 다이어그램은 눈으로만 훑었습니다.
- 위험한 동작 파악
- 격리 절차 설계
- 가짜가 실제로 불렸는지 마커로 확인
- 임계값이면 그리드로 확정
- 회귀 테스트로 고정
추천
격리했다는 가정을 코드에 넣지 말고, 가짜가 실제로 호출됐는지를 테스트 결과 표의 열로 만듭니다. 임계값은 그리드로 정하고, 렌더가 필요 없는 검증은 렌더 없이 합니다.
이유
PATH에 동명 가짜 바이너리를 두는 것만으로는 격리되지 않습니다. 호출부가
require.resolve('wrangler/package.json')처럼 진입점을 직접 잡으면 PATH를
비켜갑니다. 모듈 해석은 상위 디렉터리로 올라갑니다. 워크트리가
<repo>/.claude/worktrees/에 생기므로 로컬 node_modules를 지워도 상위 저장소의 진짜
CLI가 잡힐 수 있습니다. 파괴적 명령이면 실제로 프로덕션에 닿습니다. 저장소 밖 임시 디렉터리로
복사하고, 실행 전에 createRequire(...).resolve()가 실패하는지 단언합니다. 실패하지
않으면 중단합니다.
“가짜가 실제로 불렸나”를 결과 표의 열로 만듭니다. 마커를 심어두고 매 케이스 grep하지
않으면, 프로덕션을 실제로 치고도 “통과”로 보이는 테스트가 만들어집니다. 부작용 판정은
shasum 전후 비교로 합니다. 기대값을 미리 표에 적어두면 하네스 자체의 버그도 그 표에서
드러납니다.
임계값은 그리드로 정합니다. 무관 입력 세트(오탐이 0이어야 한다)와 관련 입력 세트(적중해야 한다)를 각각 만들고 교차시켜 값을 정합니다. 경계에서 뒤집힌 케이스는 회귀 테스트로 고정합니다. 임계값은 나중에 다시 만지게 되고, 그때 고정해 두지 않은 경계 케이스는 조용히 사라집니다.
커밋할 다이어그램은 눈으로 훑지 말고 mermaid.parse()로 렌더 없이 문법 검증합니다.
mermaid와 jsdom만 있으면 됩니다. 검증 대상은 손에 든 문자열이 아니라 파일에서 다시
뽑은 펜스 블록이어야 하고, 머지 충돌을 해소한 뒤에는 재검증합니다. Node 22에서
globalThis.navigator는 getter-only라 Object.defineProperty로 주입해야 하고, mermaid는
그 전역 세팅이 끝난 뒤 await import()합니다.
예시
파괴적 CLI를 테스트할 때 격리를 단언으로 확인합니다.
const { createRequire } = require('module');try { createRequire(testDir + '/index.js').resolve('wrangler/package.json'); throw new Error('격리 실패 — 상위 node_modules가 잡혔다');} catch (e) { // MODULE_NOT_FOUND여야 통과}결과 표에 “가짜 호출 여부” 열을 둡니다.
| 케이스 | exit code | 가짜 호출됨 | 부작용 없음(shasum 동일) |
|---|---|---|---|
| 정상 입력 | 0 | 예 | 예 |
| 잘못된 입력 | 1 | 예 | 예 |
다이어그램은 렌더 없이 문법만 검증합니다.
Object.defineProperty(globalThis, 'navigator', { value: {}, configurable: true });const mermaid = await import('mermaid');mermaid.default.parse(fenceBlockFromFile); // 파일에서 다시 뽑은 블록하지 말 것
PATH에 가짜 바이너리를 두고 격리됐다고 믿기. 호출부가 진입점을 직접 resolve하면 PATH를 비켜갑니다.
가짜가 불렸는지 확인하지 않고 exit code만 초록이면 통과시키기. 프로덕션을 실제로 치고도 통과로 보일 수 있습니다.
임계값을 감으로 정하고 경계 케이스를 회귀 테스트로 안 남기기. 다음에 임계값을 만질 때 그 경계 케이스가 조용히 사라집니다.