개발 도구/AI 코딩 도구

AI 에이전트한테 테스트 코드를 통째로 짜게 시켜봤더니 — 6년차가 거른 '가짜 초록불' 5가지

stackD 2026. 8. 1. 18:00
반응형

 

LLM이 생성한 단위 테스트의 mutation score는 실무에 가까운 함수를 모은 벤치마크에서 평균 40% 안팎에 그쳤다고 합니다(Red Hat Research 소개, arXiv 2508.00408). 변이의 절반 이상, 약 60%를 놓친다는 얘기인데요. 6년차가 거른 '가짜 초록불' 5가지를 정리했습니다.

 

그럴듯해서 더 위험한 AI 테스트 코드

지난 봄에 사이드 프로젝트에서 클로드 코드(Claude Code)한테 단위 테스트를 통째로 맡겨봤어요. 구현 속도가 3배 빨라지니까 욕심이 나더라고요. 처음엔 커버리지가 91%까지 올라서 흐뭇하게 PR을 머지했는데, 두 달 뒤에 결제 모듈에서 회귀 버그가 터졌습니다.

 

문제는 사람 코드보다 AI 테스트가 더 깔끔해 보인다는 점이었어요. 변수명, 주석, 구조까지 너무 정돈돼 있어서 리뷰 본능이 마비됩니다. "이 정도면 됐겠지" 하고 슥 넘기게 되더라고요.

 

동어반복 테스트, Mock이 자기를 검증하는 패턴

AI가 가장 자주 만드는 안티패턴입니다. userRepo.findById.mockReturnValue({id:1})로 mock을 깔아둔 뒤에 expect(result.id).toBe(1)로 검증해요. 실제 구현 코드를 return null로 바꿔도 그대로 통과하는 테스트입니다.

 

AI는 명세가 아니라 현재 코드의 출력을 정답으로 삼는 경우가 많거든요. 버그가 들어있는 로직이라도 그 결과를 그대로 기대값에 박제하기도 합니다. 사이드 프로젝트에 StrykerJS를 돌렸더니 평균 38%, 결제 도메인은 22%라는 mutation score가 나왔어요. 100개 변이 중 78개를 놓친다는 뜻입니다.

 

 

무한 모킹으로 통합 오류 못 잡는 AI 테스트 코드

DB, HTTP, 파일 시스템까지 전부 mock으로 덮어버리는 경우입니다. MSR '26 Hora 논문에 따르면 AI 에이전트 커밋이 사람 커밋보다 mock 추가 비율이 유의미하게 높다고 해요. "코드가 mock을 정해진 순서로 호출했는지"만 검증하니까 정작 통합 시점에서 터질 오류는 안 보이는 구조죠.

 

리뷰 휴리스틱 하나만 박아두시면 됩니다. Mock 설정 라인을 Assert 라인으로 나눈 비율이 2를 넘으면 과도한 모킹 의심. 저는 이 비율을 CI 경고 룰로 걸어두고 있어요.

 

 

해피패스 외길과 엣지 케이스 60% 누락 문제

빈 배열, null, 타임아웃, 동시성, 권한 분기. 제가 6개월간 사이드 프로젝트 PR에서 직접 카운트해봤더니 AI가 자발적으로 엣지 케이스를 짜주는 비율이 30~40% 수준이었고, 401/403 인증 분기는 명시적으로 요구하지 않으면 거의 안 만들더라고요.

 

안드로이드 쪽은 더 심합니다. 화면 회전, 백그라운드 복귀 같은 라이프사이클 상태를 빠뜨리고, 비동기 대기에는 Thread.sleep(500)을 박는 안티패턴까지 그대로 가져와요. 코루틴 테스트라면 runTest { ... } 안에서 advanceUntilIdle()을 호출하는 쪽이 정석인데 그 자리에 sleep이 들어앉아 있는 식입니다.

 

 

Flaky Green, 간헐적으로 통과하는 테스트의 함정

await 누락, 고정 안 된 랜덤 시드, 시간대 의존성. Bitrise Mobile Insights Report 2025가 1,000만 건이 넘는 빌드를 분석한 자료에서는 flaky test를 겪는 팀 비율이 3년간 160% 늘었다고 합니다. CI에서 retry로 자동 무시되기 쉽고, 정작 진짜 버그가 터질 때도 "또 flaky겠지" 하고 넘어가게 됩니다.

 

Date.now, Math.random, 타임존을 명시적으로 고정하지 않은 테스트는 일단 의심부터 하시면 됩니다. 시드를 박지 않은 랜덤은 자정 직전에만 깨지는 식의 시한폭탄이 되거든요.

 

사람이 꼭 봐야 할 5가지 테스트 체크포인트

리뷰 들어가실 때 이 다섯 줄만 자문해보셔도 절반 이상 걸러집니다.

 

  1. Assertion이 Mock 반환값이 아니라 계산된 결과를 검증하는가?
  2. 전체 테스트 중 예외·경계 케이스가 30% 이상인가?
  3. DB·HTTP 중 하나는 testcontainers나 MSW로 실제 호출하는가?
  4. Date.now, Math.random 같은 비결정 요소가 고정됐는가?
  5. 테스트 이름이 'works correctly'가 아니라 'returns 0 when cart is empty'처럼 명세를 담고 있는가?

 

개인적으로는 결제·인증 같은 핵심 도메인만 mutation score 70% 미만이면 머지 차단을 거는 방식을 쓰고 있어요. 빌드 시간은 4분 늘었는데, 분기별 회귀 버그가 9건에서 2건으로 줄어든 게 결정적이었습니다.

 


제 사이드 프로젝트 기준 커버리지 91% vs mutation score 22%. 이 두 숫자 사이의 간극이 결국 AI 테스트의 전부였던 셈입니다.

 

 

반응형
LIST