AI 생성 코드에 테스트 붙이기: 커버리지로 빈 경로를 찾고, 커버리지가 놓치는 규칙은 직접 고르기
node:test 커버리지 실행 결과로 보는 "100%여도 빠진 테스트"

핵심 요약
- AI가 구현과 함께 만든 테스트는 정상 경로에 치우치기 쉬우므로 커버리지로 빈 분기를 먼저 찾는다.
- Node.js는 --experimental-test-coverage로 설치 없이 줄·분기 커버리지를 보여 준다.
- Math.min 같은 함수 호출로 구현한 규칙은 지워도 커버리지가 100%로 남는다.
- 요구사항 문장과 테스트를 대조하고, 중요한 규칙은 구현에서 지워 보고 테스트가 실패하는지 확인한다.
AI 코딩 도구에 기능을 요청하면 구현과 함께 테스트도 몇 개 만들어 준다. 그런데 이 테스트는 대개 정상 경로만 확인한다. 이 글은 그런 테스트에 커버리지를 돌려 빠진 경로를 찾고, 사람이 위험 경로를 골라 테스트를 추가하는 흐름을 실제 실행 결과로 정리한다. 커버리지 100%가 "테스트가 충분하다"는 뜻이 아니라는 점도 같은 예제로 확인한다. 출력은 2026-10-07에 Node.js 24.15로 실행한 결과다.
예제: 페이지네이션 파라미터 검증
// 쿼리 문자열의 page·size를 검증해 offset을 계산한다.
export function parsePagination(query = {}) {
const page = query.page === undefined ? 1 : Number(query.page);
const size = query.size === undefined ? 20 : Number(query.size);
if (!Number.isInteger(page) || page < 1) {
throw new RangeError('page must be a positive integer');
}
if (!Number.isInteger(size) || size < 1) {
throw new RangeError('size must be a positive integer');
}
const limit = Math.min(size, 100); // 한 번에 100개 넘게 주지 않는다
return { limit, offset: (page - 1) * limit };
}
구현과 함께 생성된 테스트는 기본값과 정상 입력 두 개뿐이었다.
test('기본값', () => {
assert.deepEqual(parsePagination({}), { limit: 20, offset: 0 });
});
test('3페이지, 10개씩', () => {
assert.deepEqual(parsePagination({ page: '3', size: '10' }), { limit: 10, offset: 20 });
});
1단계: 커버리지로 빈 경로 찾기
Node.js 테스트 러너는 --experimental-test-coverage 플래그로 커버리지를 수집한다. 문서상 아직 실험 기능이지만 별도 설치 없이 쓸 수 있다.
$ node --test --experimental-test-coverage --test-reporter=spec
✔ 기본값 (1.9757ms)
✔ 3페이지, 10개씩 (0.1486ms)
ℹ pass 2
ℹ fail 0
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ pagination.mjs | 69.23 | 75.00 | 100.00 | 6-7 9-10
6-7행과 9-10행, 즉 잘못된 page와 size를 거르는 두 개의 오류 분기가 한 번도 실행되지 않았다. 테스트 두 개가 모두 통과해도 입력 검증은 검증되지 않은 상태다.
2단계: 위험 경로를 골라 테스트 추가
빈 분기를 채우는 테스트와 함께, 요구사항에 있지만 커버리지 표에는 드러나지 않는 규칙을 하나 더 골랐다. "한 번에 100개까지만"이라는 상한이다.
test('page 0과 숫자가 아닌 page는 오류', () => {
assert.throws(() => parsePagination({ page: '0' }), RangeError);
assert.throws(() => parsePagination({ page: 'abc' }), RangeError);
});
test('size 0과 소수 size는 오류', () => {
assert.throws(() => parsePagination({ size: '0' }), RangeError);
assert.throws(() => parsePagination({ size: '2.5' }), RangeError);
});
test('size는 100으로 제한된다 (커버리지가 요구하지 않는 규칙)', () => {
assert.deepEqual(parsePagination({ page: '2', size: '500' }), { limit: 100, offset: 100 });
});
$ node --test --experimental-test-coverage --test-reporter=spec
✔ 기본값 (2.0817ms)
✔ 3페이지, 10개씩 (0.1227ms)
✔ page 0과 숫자가 아닌 page는 오류 (0.4359ms)
✔ size 0과 소수 size는 오류 (0.1479ms)
✔ size는 100으로 제한된다 (커버리지가 요구하지 않는 규칙) (0.1558ms)
ℹ pass 5
ℹ fail 0
ℹ pagination.mjs | 100.00 | 100.00 | 100.00 |
커버리지 100%가 놓치는 것
상한 테스트가 정말 필요한지 확인하려고 구현에서 Math.min(size, 100)을 지우고 다시 실행했다.
$ node --test --experimental-test-coverage --test-reporter=spec # 상한 규칙을 지운 구현
✔ 기본값 (2.5755ms)
✔ 3페이지, 10개씩 (0.1246ms)
✔ page 0과 숫자가 아닌 page는 오류 (0.4129ms)
✔ size 0과 소수 size는 오류 (0.1125ms)
✖ size는 100으로 제한된다 (커버리지가 요구하지 않는 규칙) (1.1809ms)
ℹ pass 4
ℹ fail 1
ℹ pagination.mjs | 100.00 | 100.00 | 100.00 |
규칙을 지워도 커버리지는 여전히 100%다. Math.min은 분기가 아니라 함수 호출이라, 그 줄이 실행되기만 하면 커버리지 도구는 "검증됨"으로 센다. 상한 테스트를 따로 쓰지 않았다면 규칙이 사라져도 아무것도 실패하지 않는다. 커버리지는 "실행되지 않은 코드"를 알려 줄 뿐이고, "요구사항이 검증되었는가"는 사람이 요구사항 목록과 테스트를 대조해야 알 수 있다.
워크플로우로 정리하기
- 기능을 요청할 때 테스트 관점 목록(정상, 경계, 잘못된 입력, 상한·하한 규칙)을 먼저 받는다.
- 구현과 테스트를 받은 뒤 커버리지를 실행해 빈 분기를 찾는다.
- 빈 분기를 채우는 테스트를 추가한다.
- 요구사항 문장마다 대응하는 테스트가 있는지 표로 대조한다. 커버리지가 100%여도 이 단계는 건너뛰지 않는다.
- 중요한 규칙은 이 글처럼 구현에서 지워 보고 테스트가 실패하는지 확인한다.
| 확인 방법 | 찾을 수 있는 것 | 찾을 수 없는 것 |
|---|---|---|
| 커버리지 | 한 번도 실행되지 않은 줄·분기 | 실행은 됐지만 결과를 확인하지 않은 규칙 |
| 요구사항 대조 | 테스트가 없는 요구사항 | 요구사항에 없던 예외 상황 |
| 규칙을 지워 보기 | 그 규칙을 지키는 테스트가 실제로 있는지 | 다른 규칙의 누락 |
규칙을 지워 보는 방식을 자동화한 것이 뮤테이션 테스트다. 모든 규칙에 손으로 적용하기는 어렵지만, 결제 금액이나 권한처럼 중요한 규칙 몇 개에는 손으로도 충분히 할 수 있다. 테스트를 구현보다 먼저 쓰는 흐름은 AI 코딩 어시스턴트와 TDD에서, 타입으로 잘못된 입력을 줄이는 방법은 TypeScript와 AI 코딩 품질에서 다룬다.
초보자가 자주 실수하는 포인트
- 생성된 테스트가 모두 통과하면 검증이 끝났다고 여긴다.
- 커버리지 100%를 목표로 삼아 의미 없는 테스트를 추가한다.
- 상한·하한 같은 규칙을 테스트하지 않아 규칙이 사라져도 모른다.
- 오류 분기를 테스트하지 않아 입력 검증이 동작하는지 확인하지 않는다.
체크리스트
- 테스트 관점 목록을 구현 전에 받았다.
- 커버리지를 실행해 빈 분기를 확인했다.
- 요구사항 문장마다 대응하는 테스트가 있다.
- 중요한 규칙 하나 이상을 지워 보고 테스트 실패를 확인했다.
자주 묻는 질문
커버리지 목표치는 얼마로 잡아야 하나요?
숫자보다 빈 분기가 무엇인지 보는 용도로 쓰는 것이 좋습니다. 이 글의 예처럼 100%여도 규칙이 빠질 수 있으므로, 목표치를 두더라도 요구사항 대조와 함께 운영합니다.
Jest나 Vitest에서도 같은 방법을 쓸 수 있나요?
네. 두 도구 모두 커버리지 보고서를 제공합니다. 커버리지가 실행 여부만 알려 준다는 점은 도구와 관계없이 같습니다.
참고 자료 · 검증 기준
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.