AI 생성 코드에 테스트 붙이기: 커버리지로 빈 경로를 찾고, 커버리지가 놓치는 규칙은 직접 고르기

node:test 커버리지 실행 결과로 보는 "100%여도 빠진 테스트"

AI 코드에 테스트 붙이기 썸네일

핵심 요약

  1. AI가 구현과 함께 만든 테스트는 정상 경로에 치우치기 쉬우므로 커버리지로 빈 분기를 먼저 찾는다.
  2. Node.js는 --experimental-test-coverage로 설치 없이 줄·분기 커버리지를 보여 준다.
  3. Math.min 같은 함수 호출로 구현한 규칙은 지워도 커버리지가 100%로 남는다.
  4. 요구사항 문장과 테스트를 대조하고, 중요한 규칙은 구현에서 지워 보고 테스트가 실패하는지 확인한다.

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은 분기가 아니라 함수 호출이라, 그 줄이 실행되기만 하면 커버리지 도구는 "검증됨"으로 센다. 상한 테스트를 따로 쓰지 않았다면 규칙이 사라져도 아무것도 실패하지 않는다. 커버리지는 "실행되지 않은 코드"를 알려 줄 뿐이고, "요구사항이 검증되었는가"는 사람이 요구사항 목록과 테스트를 대조해야 알 수 있다.

워크플로우로 정리하기

  1. 기능을 요청할 때 테스트 관점 목록(정상, 경계, 잘못된 입력, 상한·하한 규칙)을 먼저 받는다.
  2. 구현과 테스트를 받은 뒤 커버리지를 실행해 빈 분기를 찾는다.
  3. 빈 분기를 채우는 테스트를 추가한다.
  4. 요구사항 문장마다 대응하는 테스트가 있는지 표로 대조한다. 커버리지가 100%여도 이 단계는 건너뛰지 않는다.
  5. 중요한 규칙은 이 글처럼 구현에서 지워 보고 테스트가 실패하는지 확인한다.
확인 방법찾을 수 있는 것찾을 수 없는 것
커버리지한 번도 실행되지 않은 줄·분기실행은 됐지만 결과를 확인하지 않은 규칙
요구사항 대조테스트가 없는 요구사항요구사항에 없던 예외 상황
규칙을 지워 보기그 규칙을 지키는 테스트가 실제로 있는지다른 규칙의 누락

규칙을 지워 보는 방식을 자동화한 것이 뮤테이션 테스트다. 모든 규칙에 손으로 적용하기는 어렵지만, 결제 금액이나 권한처럼 중요한 규칙 몇 개에는 손으로도 충분히 할 수 있다. 테스트를 구현보다 먼저 쓰는 흐름은 AI 코딩 어시스턴트와 TDD에서, 타입으로 잘못된 입력을 줄이는 방법은 TypeScript와 AI 코딩 품질에서 다룬다.

초보자가 자주 실수하는 포인트

  • 생성된 테스트가 모두 통과하면 검증이 끝났다고 여긴다.
  • 커버리지 100%를 목표로 삼아 의미 없는 테스트를 추가한다.
  • 상한·하한 같은 규칙을 테스트하지 않아 규칙이 사라져도 모른다.
  • 오류 분기를 테스트하지 않아 입력 검증이 동작하는지 확인하지 않는다.

체크리스트

  • 테스트 관점 목록을 구현 전에 받았다.
  • 커버리지를 실행해 빈 분기를 확인했다.
  • 요구사항 문장마다 대응하는 테스트가 있다.
  • 중요한 규칙 하나 이상을 지워 보고 테스트 실패를 확인했다.

자주 묻는 질문

커버리지 목표치는 얼마로 잡아야 하나요?

숫자보다 빈 분기가 무엇인지 보는 용도로 쓰는 것이 좋습니다. 이 글의 예처럼 100%여도 규칙이 빠질 수 있으므로, 목표치를 두더라도 요구사항 대조와 함께 운영합니다.

Jest나 Vitest에서도 같은 방법을 쓸 수 있나요?

네. 두 도구 모두 커버리지 보고서를 제공합니다. 커버리지가 실행 여부만 알려 준다는 점은 도구와 관계없이 같습니다.

참고 자료 · 검증 기준

위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.

이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.