본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 25. 22:24

AI에게 테스트 작성을 맡기고 만족하고 있지는 않나요? 커버리지(Coverage)를 '측정'하는 것만으로 누락된 부분을 모두 볼 수 있습니다

요약

AI 코딩 에이전트 시대에 테스트 작성을 AI에게 맡길 때, 테스트 통과 여부(Pass)만 믿지 말고 커버리지(Coverage) 측정을 통해 누락된 분기를 확인해야 합니다. Vitest의 커버리지 기능을 활용해 경계값과 미통과 분기를 시각화함으로써 테스트의 완전성을 확보하는 방법을 제시합니다.

핵심 포인트

  • AI가 작성한 테스트가 통과(Pass)되었다고 해서 모든 사양이 충족된 것은 아님
  • 커버리지(Coverage) 측정을 통해 테스트가 놓친 코드 라인과 분기를 식별 가능
  • 테스트 리뷰의 초점을 구현이 아닌 '테스트 커버리지'와 '경계값 검증'으로 전환
  • Vitest의 --coverage 옵션을 활용해 미통과 분기를 기계적으로 확인

벤처 개발처럼, 테스트 따위 제대로 작성해 오지 않았다. 버그가 나오면 수정한다, 그렇게 운영해 온 편이다...

이 방식은, 운영 팀의 부담이 심상치 않다. 버그 대응에 쫓겨 적당히 머지(Merge)하고 "결과가 작동하지 않잖아!"라고 되는 안건은 많지 않을까?

하물며 AI 코딩의 시대이기에 점점 더 운영이 어려워지고 있을 것이다.

그래서 생각했다. 코딩 에이전트(Agent)가 있는 시대에, 인간이 테스트를 작성할 의미가 있을까?

테스트는 전부 에이전트에게 작성하게 하고, 인간은 사양(Specification)만 확인한다——그런 운영으로 밀어붙여도 괜찮지 않을까, 하고.

이 기사는 그 발상으로 "운영자가 한계까지 손을 빼려면 어떻게 해야 하는가"를 끝까지 파고든 이야기다.

핵심은, AI에게 작성하게 한 테스트의 "충족"을 어떻게 담보할 것인가이다. 결론부터 말하자면, 리뷰를 「구현」에서 「테스트」로 옮기고, 그 충분함은 「커버리지(Coverage)를 측정하는 것」만으로 크게 전진할 수 있다.

"테스트를 작성하고는 있지만" 「얼마나 망라하고 있는지는 측정한 적이 없다"는 분들은 많지 않을까?

vitest라면 --coverage

를 추가하기만 하면 된다.

npx vitest run --coverage

그러면, 어느 행(Line)·어느 분기(Branch)를 테스트가 통과하지 못했는지가 표로 팍 나타난다. 이것만으로도 "내 테스트, 여기가 빠져 있었구나"를 눈으로 볼 수 있게 된다. AI에게 테스트를 작성하게 했을 때야말로 이것이 효과적이다.

실제로 해보자.

// 사양: 5000엔 이상은 무료 배송. 홋카이도·오키나와는 1000엔, 그 외에는 500엔.
export function shippingFee(prefecture: string, amount: number): number {
if (amount >= 5000) return 0;
...

"제대로 테스트해 줘"라고 하면 사람도 AI도 놓치기 때문에, 테스트 케이스는 기계적으로 나열한다. 요점은 경계(Boundary)와 분기를 전부 찌르는 것뿐이다.

prefectureamount기대값
도쿄50000 ← 무료의 경계 지점 딱 맞음
...
이 표가 이후 모든 "정답"이 된다. 인간이 리뷰하는 것은 이 표만이면 된다는 것이 이 기사의 결론으로 이어진다.

AI에게 대충 시키면, 대개 "그럴싸하게 초록색이 되는" 테스트가 돌아온다.

import { describe, it, expect } from "vitest";
import { shippingFee } from "./shipping";
describe("shippingFee", () => {
...

실행하면——테스트는 초록색. 2건 모두 패스(Pass). 보통이라면 여기서 "OK"라고 말하며 머지하고 싶어진다.

여기서 --coverage

를 추가한다. 그러면 다음과 같다(실측).

Test Files 1 passed (1)
Tests 2 passed (2)
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
...

초록색인데, Branch는 83%. Uncovered Line #s 에 「4」.

4행目は 홋카이도·오키나와라면 1000엔

의 분기다. 즉 AI의 테스트는 도서 지역 케이스를 한 번도 통과하지 않았다. 초록색임에도 불구하고, 사양의 3분의 1이 빠져 있었다.

테스트가 초록색인지 여부는 "작성한 테스트가 통과했는가"만을 말해준다. 무엇을 테스트하는 것을 잊었는지는 커버리지를 측정하지 않으면 보이지 않는다.

커버리지가 가리킨 "4행目(도서 지역)"과, 덤으로 경계값(4999)을 표에서 추가한다.

import { describe, it, expect } from "vitest";
import { shippingFee } from "./shipping";
describe("shippingFee", () => {
...

재실행(실측).

Tests 5 passed (5)
Statements : 100% ( 5/5 )
Branches : 100% ( 6/6 )

Branch 100%. 이것으로 "사양의 분기는 전부 통과했다"라고 숫자로 말할 수 있다.

포인트는, 커버리지 100%를 목표로 하는 것이 아니라, 「누락을 찾아내는 도구」로서 사용하는 것이다. 표(사양)를 정답으로 삼아, 누락을 없애기 위해 측정한다.

이 단계에 이르면, 인간의 업무는 「테스트 표(=사양)의 리뷰」와 「커버리지 (Coverage) 확인」으로 거의 집약된다. 구현된 코드를 일일이 따라갈 필요가 없어진다. 이것이 우리가 원했던 것이다.

하지만 커다란 함정이 있다. AI에게 구현과 테스트를 “동시에” 작성하게 하면, 테스트가 구현에 치우친 “동의반복 (Tautology)”이 된다. 구현의 버그까지 테스트가 그대로 추인해 버려서, 테스트 결과는 통과(Green)인데 실제로는 틀린 상태가 된다.

대책은 심플하다.

테스트는 사양으로부터 작성한다. 구현을 보여주지 않는다. 앞서 언급한 표만이 정답이다.

테스트를 먼저 확정시킨 후 구현하게 한다 (테스트 퍼스트 (Test-First)).

가능하다면
구현과 테스트를 별도의 세션/별도의 에이전트로 작성하게 한다. 서로의 버그를 공유하지 않도록 한다.

개인적으로는, AI에게 "우선 이 표를 만족하는 테스트만 작성해줘"라고 전달하고, 테스트가 통과하는 것을 확인한 뒤에 구현을 시키는 것만으로도 사고가 상당히 줄어들었다.

1. 사양 → 테스트 케이스를 기계적으로 열거 ← 인간이 리뷰하는 지점
2. AI에게 테스트를 작성하게 한다 (구현은 보여주지 않음 / 테스트 퍼스트)
3. AI에게 구현을 작성하게 하여 통과(Green)시킨다
...

도구: vitest/--coverage

(내부는 V8 또는 istanbul). 우선은 이것만으로도 충분하다.

  • E2E·성능·보안·탐색적 테스트는 이 범주 밖에 있다.

  • 그리고 중요한 주의사항:
    커버리지 100% = 완벽함, 이 아니다. "모든 행을 통과했다"는 것일 뿐, "그 행이 올바른가"까지는 보장하지 않는다. 예를 들어 >=>로 잘못 작성해도 100%인 상태로 빠져나갈 수 있다.

  • 그 부분을 해결하기 위해
    **뮤테이션 테스트 (Mutation Testing)**라는 다음 레이어가 있으며, 그런 단계까지 나아가면 코드 리뷰의 대상이 대폭 줄어들고 본질적인 사양에 무게를 둘 수 있게 된다.

  • 그 부분을 해결하기 위해

그럼에도 불구하고, 「구현을 전부 읽는 것」에서 「테스트 표와 커버리지를 보는 것」으로 중심을 옮기는 것만으로도 AI 코드 리뷰는 정말로 차원이 달라진다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0