잘 사용하면 AI는 테스트의 열거 부분에 탁월합니다 — edge case를 brainstorm하고 boilerplate를 빠르게 작성합니다. 하지만 assertion이 의미 있는지 검증해야 합니다. AI는 아무것도 증명하지 못하면서 통과하는 테스트를 기꺼이 쓰기 때문입니다.
// 테스트 대상 함수, contract 포함:
// applyDiscount(price, percent) -> price를 percent만큼 감소.
// Contract: percent는 0..100이어야 함; 아니면 RangeError를 던짐. price >= 0.
function applyDiscount(price, percent) {
if (percent < 0 || percent > 100) throw new RangeError('percent out of range');
return price - (price * percent) / 100;
}
// AI가 제안한 테스트 케이스(Jest) — happy path만이 아니라 경계와 에러 경로에 주목:
test('applies a normal discount', () => {
expect(applyDiscount(100, 20)).toBe(80); // happy path
});
test('0% leaves price unchanged', () => {
expect(applyDiscount(100, 0)).toBe(100); // 경계: 하한
});
test('100% makes it free', () => {
expect(applyDiscount(100, 100)).toBe(0); // 경계: 상한
});
test('rejects percent above 100', () => {
expect(() => applyDiscount(100, 150)).toThrow(RangeError); // 에러 경로
});
AI는 경계 케이스(0과 100)와 당신이 잊을 수 있는 에러 경로를 제안했습니다. 당신의 일은 toBe(80)이 올바른 기대값인지 확인하는 것이지, 함수가 우연히 반환하는 값이 아닙니다.
테스트의 어려운 부분은 test(...) 블록을 타이핑하는 것이 아니라 그렇지 않으면 놓칠 케이스를 생각해내는 것이고, AI는 그 폭에서 정말 뛰어납니다. 하지만 당신이 말해주지 않으면 코드가 무엇을 해야 하는지 전혀 모르므로, 방치하면 구현을 그대로 비추는 테스트(통과하지만, 함수가 틀려도 통과할)를 쓰는 경향이 있습니다. AI를 edge-case 생성기로 쓰면서 당신이 assertion을 소유하면 넓은 커버리지와 진짜 정확성을 얻습니다 — 속도는 AI에서, 판단은 당신에게서.