AI 생성 테스트 사례: LLM을 자동 테스트 작성기로 사용
테스트 작성은 지루하고 시간이 많이 걸리며 미루는 일입니다. PR을 몇 번이나 보셨나요? 단 한 번의 새로운 테스트도 없이 왜 "백로그에 추가"했습니까? LLM이 이것을 바꾸고 있습니다 시나리오: GitHub Copilot, Claude 및 GPT-4는 놀라운 품질의 테스트 사례를 생성할 수 있습니다. 소스 코드, 사용자 스토리 또는 API 사양에서 시작하여 작성 시간 단축 테스트를 40~60% 줄이고 건너뛰게 만드는 심리적 장벽을 낮춰줍니다.
그러나 AI에게 "테스트 작성"을 요청하고 이를 맹목적으로 사용하는 것만으로는 충분하지 않습니다. 이 가이드에서는 고품질 테스트를 생성하고 검증하며 CI 파이프라인에 통합하는 구조화된 워크플로입니다.
무엇을 배울 것인가
- 효과적인 테스트 사례를 생성하기 위한 전략 촉구
- 소스 코드에서 단위 테스트를 위한 GitHub Copilot 워크플로
- 사용자 스토리 및 BDD 사양을 바탕으로 테스트 생성
- AI가 생성한 테스트의 품질을 검증하는 방법
- 실제 가치를 검증하기 위해 돌연변이 테스트와 통합
- 한계와 위험: AI 테스트가 충분하지 않은 경우
검증 없이 AI가 작성한 테스트의 문제점
워크플로에 들어가기 전에 중요한 경고: LLM은 다음과 같은 테스트를 생성합니다. 그들은 통과한다
쉽게 올바른 동작을 테스트할 필요는 없지만 반드시 테스트할 필요는 없습니다. 가장 일반적인 패턴은
"팬텀 테스트" — 이를 확인하는 테스트 result !== null 확인하는 대신
실제 가치.
// ESEMPIO DI TEST AI GENERATO SCARSO (da evitare)
// Copilot ha generato questo — sembra un test, ma non verifica nulla di utile
import { calculateDiscount } from './pricing';
describe('calculateDiscount', () => {
it('should return a value', () => {
const result = calculateDiscount(100, 'PROMO10');
expect(result).toBeDefined(); // INUTILE: non verifica il valore corretto
expect(result).not.toBeNull(); // INUTILE: solo dice "non e null"
expect(typeof result).toBe('number'); // DEBOLE: non verifica che sia 90
});
});
// ESEMPIO DI TEST AI GENERATO BUONO (con prompt corretto)
describe('calculateDiscount', () => {
it('applica sconto PROMO10 del 10% sul prezzo base', () => {
expect(calculateDiscount(100, 'PROMO10')).toBe(90);
});
it('restituisce il prezzo originale per codice non valido', () => {
expect(calculateDiscount(100, 'INVALID')).toBe(100);
});
it('applica sconto massimo del 50% anche per codici piu generosi', () => {
expect(calculateDiscount(100, 'SUPER80')).toBe(50); // cap al 50%
});
it('gestisce prezzi a zero senza errori', () => {
expect(calculateDiscount(0, 'PROMO10')).toBe(0);
});
it('lancia errore per prezzi negativi', () => {
expect(() => calculateDiscount(-10, 'PROMO10')).toThrow(RangeError);
});
});
GitHub Copilot에 대한 효과적인 프롬프트
생성된 테스트의 품질은 프롬프트의 품질에 직접적으로 좌우됩니다. 전략은 다음과 같습니다. 더 효과적인:
// STRATEGIA 1: Commento contestuale nel file di test
// Il commento guida Copilot nel generare test completi
// src/utils/string-validator.ts
export function validateEmail(email: string): boolean {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
export function validatePassword(password: string): {
valid: boolean;
errors: string[];
} {
const errors: string[] = [];
if (password.length < 8) errors.push('Minimo 8 caratteri');
if (!/[A-Z]/.test(password)) errors.push('Almeno una maiuscola');
if (!/[0-9]/.test(password)) errors.push('Almeno un numero');
if (!/[!@#$%]/.test(password)) errors.push('Almeno un carattere speciale');
return { valid: errors.length === 0, errors };
}
// src/utils/string-validator.test.ts
// Test per validateEmail e validatePassword.
// Coprire: email valide, email non valide (mancano @, dominio, TLD),
// password valida, password troppo corta, password senza maiuscola,
// senza numero, senza speciale, stringa vuota, valori limite.
// Usare describe nested per raggruppare casi validi e invalidi.
// I test devono essere deterministici, no mocks necessari.
import { validateEmail, validatePassword } from './string-validator';
describe('validateEmail', () => {
describe('email valide', () => {
test.each([
['user@example.com'],
['user.name+tag@company.co.uk'],
['123@numbers.org'],
])('%s deve essere valida', (email) => {
expect(validateEmail(email)).toBe(true);
});
});
describe('email non valide', () => {
test.each([
['not-an-email'],
['missing@'],
['@nodomain.com'],
['spaces in@email.com'],
[''],
])('%s deve essere non valida', (email) => {
expect(validateEmail(email)).toBe(false);
});
});
});
describe('validatePassword', () => {
it('password valida supera tutti i controlli', () => {
const result = validatePassword('SecureP@ss1');
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('password troppo corta produce errore specifico', () => {
const result = validatePassword('Ab1@');
expect(result.valid).toBe(false);
expect(result.errors).toContain('Minimo 8 caratteri');
});
});
Claude와 함께하는 사용자 스토리 생성
Claude는 사용자 스토리를 구조화된 테스트 사례로 변환하는 데 특히 효과적입니다. 개발자가 잊어버리는 경향이 있는 극단적인 경우도 포함됩니다.
"""
Prompt per Claude: Generazione test da User Story
"""
user_story = """
User Story: Come utente registrato, voglio aggiungere prodotti al carrello
in modo da poter procedere all'acquisto.
Criteri di accettazione:
- L'utente puo aggiungere un prodotto disponibile al carrello
- Il contatore nel header si aggiorna immediatamente
- Non si possono aggiungere prodotti esauriti
- La quantita massima per prodotto e 10
- Un prodotto gia nel carrello aumenta la quantita (non crea duplicato)
- I prodotti nel carrello persistono dopo refresh della pagina
"""
prompt = f"""Sei un QA engineer esperto in BDD e test automation.
Data la seguente user story e criteri di accettazione, genera:
1. Casi di test Playwright in TypeScript per i test E2E
2. Unit test per la funzione addToCart(productId, quantity) in Vitest
3. Test cases in formato Gherkin (BDD) per la documentazione
Per ogni test specifica:
- Scenario (nome descrittivo)
- Precondizioni
- Azioni
- Risultato atteso
Includi casi happy path, edge cases e casi di errore.
User Story:
{user_story}
Genera i test nel formato richiesto, con commenti che spiegano il
rationale di ogni caso di test."""
# Risposta Claude genera 15-20 test cases strutturati con Playwright + Vitest + Gherkin
# Esempio output Claude in Gherkin
Feature: Aggiunta prodotti al carrello
Background:
Given l'utente e autenticato
And il catalogo prodotti e disponibile
Scenario: Aggiunta prodotto disponibile
When l'utente clicca "Aggiungi al carrello" sul prodotto "Laptop Pro X"
Then il carrello contiene 1 unita di "Laptop Pro X"
And il contatore nell'header mostra "1"
Scenario: Prodotto gia nel carrello
Given il carrello contiene 1 unita di "Laptop Pro X"
When l'utente clicca "Aggiungi al carrello" sullo stesso prodotto
Then il carrello contiene 2 unita di "Laptop Pro X"
And non viene creato un record duplicato
Scenario: Tentativo aggiunta prodotto esaurito
Given il prodotto "Tablet Z" ha disponibilita 0
Then il bottone "Aggiungi al carrello" per "Tablet Z" e disabilitato
Scenario: Limite quantita massima
Given il carrello contiene 10 unita di "Mouse Wireless"
When l'utente tenta di aggiungere un'altra unita
Then viene mostrato il messaggio "Quantita massima raggiunta (10)"
And la quantita rimane 10
완전한 워크플로우: 코드에서 검증된 테스트까지
// Workflow in 5 step per test AI-generated di qualita
// STEP 1: Genera i test
// Prompt: "Genera test completi per questo servizio con casi happy path,
// edge cases, casi di errore. Usa Vitest e TypeScript."
// [AI genera tests/payment.service.test.ts]
// STEP 2: Esegui i test generati
// npm run test -- tests/payment.service.test.ts
// ATTESO: tutti passano (se l'implementazione e corretta)
// STEP 3: Verifica mutation score
// npx stryker run -- solo sui file modificati
// TARGET: mutation score > 60% sui test AI-generated
// STEP 4: Review manuale
const REVIEW_CHECKLIST = `
[ ] I test coprono i criteri di accettazione?
[ ] I test verificano il comportamento (non l'implementazione)?
[ ] Ci sono edge cases mancanti? (null, undefined, stringhe vuote, valori limite)
[ ] I test sono deterministici? (no Date.now(), no Math.random() senza mock)
[ ] I test sono isolati? (no dipendenze tra test)
[ ] I nomi dei test descrivono il comportamento atteso?
[ ] I test falliscono correttamente se implementazione e errata?
`;
// STEP 5: Aggiungi test mancanti
// Usa mutation report per identificare mutanti non uccisi
// Aggiungi test mirati per i mutanti sopravvissuti
돌연변이 테스트로 AI 테스트 검증
AI가 생성한 테스트가 실제 가치가 있는지 확인하는 가장 신뢰할 수 있는 방법은 다음과 같습니다. 돌연변이 테스트를 수행합니다.
// stryker.config.js — configurazione per analizzare test AI-generated
/** @type {import('@stryker-mutator/api/core').PartialStrykerOptions} */
module.exports = {
testRunner: 'vitest',
coverageAnalysis: 'perTest',
mutate: [
// Muta solo i file per cui stiamo analizzando i test
'src/services/payment.service.ts',
'src/utils/pricing.ts'
],
vitest: {
configFile: 'vitest.config.ts'
},
thresholds: {
high: 80,
low: 60,
break: 50 // Fallisce la build se mutation score scende sotto 50%
},
reporters: ['html', 'json', 'clear-text'],
htmlReporter: {
fileName: 'reports/mutation-report.html'
}
};
AI 테스트 생성의 위험과 한계
- 구현을 테스트하는 테스트: AI는 논리를 복제하는 경향이 있습니다. 동작을 확인하는 대신 테스트에서 코드를 사용합니다. 테스트 내용을 검토하세요. 소스 코드의 "미러"
- 도메인 지식 부족: AI는 비즈니스 규칙을 모른다 귀하의 특정 도메인의 중요한 비즈니스 로직 테스트를 작성하거나 검토해야 합니다. 맥락이 있는 개발자로부터
- 거짓 확신: 생성된 테스트를 통과했다고 해서 유용한 테스트를 의미하는 것은 아닙니다. 전체 적용 범위를 고려하기 전에 항상 돌연변이 테스트를 수행하십시오.
- 비품의 환각: AI는 다음과 같은 테스트 데이터를 생성할 수 있습니다. 유효하지만 유효하지 않음(불가능한 날짜, 범위를 벗어난 숫자, 공식적으로 이메일 유효하지만 의미상 부정확함)
결론
AI 테스트 생성은 올바르게 사용하면 강력한 도구입니다. 쓰기 속도를 높여줍니다. 상용구의 경우 쉽게 잊혀지는 엣지 케이스를 생성하고 장벽을 낮춥니다. 심리학부터 테스트까지. 하지만 이를 위해서는 검증 워크플로우(변이 테스트 및 검토)가 필요합니다. 수동 - 생성된 테스트가 실제로 가치가 있는지 확인합니다.
경험 법칙: AI를 사용하여 상용구 및 명백한 사례의 80%를 생성합니다. 도메인 지식이 필요한 20%를 작성합니다..







