당신의 AI는 절대 실패하지 않는 테스트를 작성합니다
AI에게 테스트를 요청합니다. AI는 12개의 통과(green)된 테스트를 내놓습니다. CI는 통과됩니다. 코드를 머지합니다. 3일 후, 버그가 운영 환경(production)에 도달합니다. 테스트를 확인해 봅니다. 테스트는 통과했지만, 아무것도 검증하지 않았습니다.
통과된 테스트(green test)는 증거가 아닙니다. 그것은 가설일 뿐입니다. AI는 절대 실패하지 않는 가설을 작성합니다.
다음 함수를 보십시오:
func Discount(total int) int {
if total > 100 {
return total - 10
}
return total
}
AI는 다음과 같은 테스트를 작성할 수도 있습니다:
func TestDiscount(t *testing.T) {
got := Discount(150)
if got < 0 {
t.Errorf("result should not be negative")
}
}
이 테스트는 통과(green)됩니다. 코드가 실행되므로 커버리지(coverage)는 좋아 보입니다. 하지만 이 테스트는 결과가 0보다 작은지만 확인합니다. 만약 할인 로직을 수학적 오류로 바꾸더라도 테스트는 여전히 통과됩니다. 동작(behavior)을 검증하는 것이 아니라, 그저 불이 켜져 있는지만 확인하는 셈입니다.
이것은 함정입니다. 커버리지는 실행된 라인 수를 셀 뿐, 중요한 어설션(assertion)을 세지 않습니다. 90%의 커버리지 보고서가 망가진 코드를 숨길 수 있습니다.
AI는 통과(green light)로 가는 최단 경로를 찾으려 합니다. 그래서 아무것도 검증하지 않는 느슨한 어설션과 모크(mock)를 선택합니다.
이를 해결하려면 한 가지 규칙만 기억하세요. 테스트가 '실패하는 법'을 알고 있는지 확인하십시오.
의도적으로 코드를 변경해 보세요. 만약 테스트가 여전히 통과된다면, 그 테스트는 쓸모없는 것입니다. 버리십시오.
진정한 테스트는 다음과 같습니다:
func TestDiscount(t *testing.T) {
if got := Discount(150); got != 140 {
t.Errorf("Discount(150) = %d, want 140", got)
}
}
로직을 망가뜨리면 이 테스트는 즉시 실패(red)합니다. 제대로 물어뜯는 것이죠.
모든 테스트를 일일이 수동으로 할 수는 없습니다. 대신 뮤테이션 테스팅(mutation testing)을 사용하세요. Go 언어의 Gremlins와 같은 도구는 코드에 작은 변화를 가합니다. 그리고 여러분의 테스트가 그 변화를 잡아내는지 확인합니다.
변경 사항이 테스트를 실패하게 만들지 못한다면, 여러분의 테스트 스위트(suite)에 구멍이 난 것입니다.
저는 AI가 작성한 코드에 이 방식을 사용합니다. AI가 작업이 끝났다고 선언하게 두지 않습니다. 게이트(gate)를 실행합니다. 도구가 코드를 변형(mutate)합니다. 만약 테스트가 여전히 통과된다면, AI는 테스트를 다시 작성해야 합니다. 테스트가 좋은지 결정하는 것은 AI가 아니라 뮤테이션입니다.
실패할 수 있음을 증명하지 못한 AI 테스트는 제 코드베이스(codebase)에 들어올 수 없습니다. 실패할 수 없는 테스트는 테스트가 없는 것보다 더 나쁩니다. 테스트가 없는 것은 명백하지만, 가짜 테스트는 위험하기 때문입니다.
통과(green light)를 맹신하지 마세요. 테스트의 가치는 그것이 만들어낼 수 있는 실패(red)에 달려 있습니다.
Source: https://dev.to/ohugonnot/your-ai-writes-tests-that-can-never-fail-3i57
선택 사항 학습 커뮤니티: https://t.me/GyaanSetuAi
