05 - Testarea codului generat de AI: strategii și cadre de calitate
2025 a stabilit un adevăr incomod: codul generat de AI este funcțional, rapid de produs, adesea elegant ca structură. Dar nu este sigur. Potrivit Securitatea codului Veracode GenAI Raport 2025, care a analizat peste 100 de LLM-uri pe 80 de sarcini de codare în Java, Python, C# și JavaScript, the 45% din codul generat de AI eșuează testele de securitate si introduce OWASP Top 10 vulnerabilități din baza de cod. 62% au defecte de design, nu erori evidente, ci erori trăsături arhitecturale profunde care apar doar în producție. Și codul produs de AI introduce Vulnerabilitate de 2,74 ori mai mare în comparație cu codul scris de dezvoltatori umani.
Aceste date nu sunt o condamnare a codării vibe. Ele sunt un memento că AI schimbă cine scrie codul, nu trebuie să-l testeze. Într-adevăr, îl amplifică. Când a Agentul AI generează 500 de linii de cod în 30 de secunde, blocajul de productivitate se schimbă de la scriere la verificare. Cine nu are o strategie robustă de testare a codului AI se termină cu o bază de cod a cărei calitate este opacă, ale cărei erori sunt imprevizibile și ale căror siguranță și un pariu.
Acest articol construiește această strategie. Prin înțelegerea tiparelor unice de eroare a codului AI, prin testare unitară, testare bazată pe proprietăți, analiză statică, SAST și testarea mutațiilor, până la o listă de verificare operațională pentru a decide când să accepte sau să respingă codul generat de un asistent. Ținta sunt dezvoltatorii care folosesc deja instrumente AI în fluxul lor de lucru și doresc să-și dezvolte încrederea de a face acest lucru profesional.
Ce vei învăța
- deoarece codul generat de AI eșuează în moduri diferite decât codul uman
- Tipuri specifice de erori: API halucinate, eroare de logică, gaură de securitate
- Testare unitară și testare bazată pe proprietăți cu pytest și ipoteză
- Analiză statică cu ESLint și SonarQube configurate pentru cod AI
- Testare de securitate cu Semgrep: reguli personalizate pentru modelele AI
- TDD cu asistent AI: ciclul Red-Green-Refactor devine Red-AI-Green-Review
- Revizuire automată a codului prin conductă multi-agenți
- Valori de calitate: acoperire, scor mutație, complexitate ciclomatică
- Lista de verificare operațională pentru a accepta sau a respinge codul AI
De ce codul AI are nevoie de teste specifice
Codul scris de un dezvoltator uman poartă cu el un model mental al sistemului. Dezvoltatorul știe ce este deasupra și sub modulul pe care îl scrie, a văzut codul rulează în producție, cunoaște marginea caselor care arde. AI nu are acest context. Opera pe modele statistice extrase din miliarde de linii de cod și generează soluții care sunt plauzibil dat promptul, nu neapărat corecta dat fiind sistemul.
Această diferență produce un profil de eroare fundamental diferit. Codul uman tinde să eșueze previzibil: un dezvoltator obosit copiază codul greșit, uită o verificare nulă, folosește logica de afaceri de ieri pentru modelul de astăzi. erori punctuale, trasabil, adesea relevat de testele existente. Codul AI eșuează structural: implementează API-ul greșit cu consecvență absolută, introduce un model de vulnerabilitate prin zeci de caracteristici, construiește o arhitectură care funcționează perfect pentru 95% din cazuri și se prăbușește pe restul de 5% cu comportamente imposibil de anticipat fără o suită de teste concepută pentru asta.
Problema încrederii sintetice
AI generează cod cu aceeași încredere, indiferent de corectitudine. A modelul care halucinează o funcție inexistentă a unei biblioteci o face cu același lucru încrederea cu care implementează un algoritm corect. Nu există niciun semn de incertitudine în textul produs. Acest lucru face ca revizuirea vizuală să nu fie de încredere: codul se pare corect pentru ca este bine scris. Doar execuția dezvăluie adevărul.
Trei caracteristici specifice ale codului AI necesită o abordare diferită de testare din testarea tradițională:
- Varianta contextuală: Produce același prompt pe baze de cod diferite cod cu profiluri de calitate foarte diferite. Testarea trebuie să fie specifică context de implementare, nu doar funcționalitate abstractă.
- Consistența internă a erorilor: Dacă AI introduce un model greșit (de exemplu, manipularea nesigură a intrărilor utilizatorului), o reproduce în toate funcțiile asemănătoare. Bugurile nu sunt izolate: sunt sistemice. Acest lucru necesită testare care caută modele, nu doar cazuri individuale.
- Cunoștințe datate: Modelele au limite de cunoștințe. Folosesc API-uri depreciate, Modele de securitate învechite, dependențe cu vulnerabilități cunoscute. Testarea trebuie să includă un strat de verificare a dependențelor curente și a modelelor de securitate.
Tipuri de erori în codul generat de AI
Înainte de a construi o strategie eficientă de testare, trebuie să știți ce căutați. Codul AI produce categorii distincte de bug-uri, fiecare cu propriile caracteristici care necesită abordări diferite de detectare.
1. API halucinate
Cel mai insidios bug și, de asemenea, cel mai documentat. AI inventează funcții, metode, parametri
și module care nu există, dar cu nume atât de plauzibile încât trec revizuirea vizuală fără ele
dificultate. Un exemplu clasic: pandas.DataFrame.filter_by_threshold() nu
există, dar sună exact ca ceva care ar trebui să existe. Codul compilează (dacă
nu există o verificare strictă a tipului), trece listing și eșuează la timpul de execuție cu a
AttributeError care apare numai atunci când funcția este apelată cu date reale.
Principala apărare împotriva API-urilor halucinate este execuția, nu revizuirea. Teste unitare care de fapt apelează codul, chiar și cu o intrare minimă, detectează imediat acest tip de eroare. Verificarea tipului static cu mypy (Python) sau TypeScript strict modul adaugă un strat de protecție pentru timpul de compilare.
2. Eroare logică și Off-by-One
Codul AI este surprinzător de bun la logica simplă și surprinzător de fragil cu logica la limita. Indici de matrice, intervale inclusive vs exclusive, condiții terminarea buclei: acestea sunt punctele în care codul AI introduce erori cele subtile pe care testele standard nu le detectează dacă nu sunt special concepute pentru acestea valori limită de testare.
3. Gaura de securitate din modele cunoscute
Raportul Veracode din 2025 documentează că LLM-urile nu reușesc să se apere împotriva XSS cu 86% a cazurilor relevante și împotriva injectării log în 88% din cazuri. Acestea nu sunt erori aleatorii: sunt o consecință a faptului că datele de antrenament conțin cantități enorme de cod vulnerabil pe care modelul a învățat să îl reproducă. injecție SQL, SSRF, traversare a căii, deserializare necorespunzătoare: modele sistematice care necesită SAST dedicat, nu doar testare funcţional.
4. Defect de proiectare arhitecturală
Cel mai greu de detectat cu teste automate. AI creează soluții care funcționează la nivel local, dar nu scalați, care au interfețele potrivite, dar responsabilitățile greșite, care respectă contractul cerut, dar încalcă principiile arhitecturale fundamentale (responsabilitate unică, separarea preocupărilor, cuplare liberă). 62% defect de design citat de Veracode se încadrează în această categorie. Detectarea necesită o combinație de metrici de complexitate, analiza dependenței și revizuirea codului structurat.
Cadrul de testare pentru codul AI
Strategia de testare unitară: testarea limită
Strategia de testare unitară pentru codul AI trebuie mutată către valorile marginale și cazuri neevidente. Codul AI gestionează bine cazul drumului fericit (și a fost instruit din milioanele de exemple ale cazului drumul fericit). Eșuează la granițe, la intrări anormale, privind tratarea erorilor.
O suită eficientă de teste unitare pentru codul AI include:
- Testare cu intrare la marginea intervalului valid (valori zero, negative, max-int)
- Testare cu intrări goale, null, None, șir gol
- Testează cu intrare care conține caractere speciale sau codare neașteptată
- Teste care verifică comportamentul în cazul unei erori (tratarea excepțiilor)
- Teste care verifică efectele secundare (modificări la starea partajată, baza de date, sistemul de fișiere)
# test_ai_generated.py
# Strategia di testing per codice generato da AI
import pytest
from hypothesis import given, strategies as st, settings
from hypothesis import HealthCheck
# Funzione generata da AI (esempio)
# def process_user_input(data: dict) -> dict:
# return {
# "name": data["name"].strip().title(),
# "age": int(data["age"]),
# "email": data["email"].lower()
# }
from my_module import process_user_input
class TestAIGeneratedBoundaries:
"""Test dei valori limite per codice AI-generated."""
def test_nominal_case(self):
"""Happy path - il caso che l'AI ha quasi certamente gestito bene."""
result = process_user_input({
"name": "mario rossi",
"age": "30",
"email": "MARIO@EXAMPLE.COM"
})
assert result["name"] == "Mario Rossi"
assert result["age"] == 30
assert result["email"] == "mario@example.com"
def test_empty_name(self):
"""Edge case: nome vuoto - spesso non gestito dall'AI."""
with pytest.raises((ValueError, KeyError)):
process_user_input({"name": "", "age": "25", "email": "a@b.com"})
def test_negative_age(self):
"""Edge case: eta negativa - l'AI spesso non valida il range."""
with pytest.raises(ValueError, match="age must be positive"):
process_user_input({"name": "Test", "age": "-5", "email": "a@b.com"})
def test_missing_required_field(self):
"""Edge case: campo mancante - gestione KeyError o default?"""
with pytest.raises(KeyError):
process_user_input({"name": "Test", "age": "25"})
def test_sql_injection_in_name(self):
"""Security: input injection non deve passare invariato."""
malicious = "'; DROP TABLE users; --"
result = process_user_input({
"name": malicious,
"age": "25",
"email": "a@b.com"
})
# Il nome deve essere sanitizzato o rifiutato
assert "DROP TABLE" not in result.get("name", "")
def test_extremely_long_input(self):
"""Edge case: input molto lungo - buffer overflow / ReDoS."""
long_name = "a" * 10000
# Non deve appendere indefinitamente o crashare
with pytest.raises((ValueError, OverflowError)):
process_user_input({"name": long_name, "age": "25", "email": "a@b.com"})
def test_unicode_name(self):
"""Compatibilità Unicode: nomi non-ASCII."""
result = process_user_input({
"name": "giuseppe gallo",
"age": "28",
"email": "giuseppe@example.it"
})
assert result["name"] == "Giuseppe Gallo"
class TestAIGeneratedWithMocks:
"""Test con mock per isolare le dipendenze."""
def test_database_not_called_on_validation_error(self, mocker):
"""Verifica che il DB non venga chiamato se la validazione fallisce."""
mock_db = mocker.patch("my_module.database.save")
with pytest.raises(ValueError):
process_user_input({"name": "", "age": "25", "email": "a@b.com"})
mock_db.assert_not_called() # L'AI spesso chiama il DB prima di validare
Testare bazată pe proprietăți cu ipoteză
Testarea bazată pe proprietăți este cel mai puternic instrument disponibil pentru testarea codului AI. În loc să definească cazuri de testare individuale, ei se definesc singuri proprietăți invariante care trebuie să fie valabil pentru orice intrare din domeniul specificat și se generează Ipoteza automat sute de intrări pentru a găsi contraexemple. Această tehnică este deosebit de eficient împotriva codului AI, deoarece explorează în mod sistematic spațiul de intrări, inclusiv cazuri marginale pe care nu ne-am fi gândit să le testăm manual.
O lucrare din 2025 publicată pe arXiv (Testare bazată pe proprietăți agentice: Găsirea erorilor În ecosistemul Python) a demonstrat că un agent Claude Code care generează Testele de ipoteză găsesc erori diferite și complementare în comparație cu testele scrise manual, cu putine false pozitive. Abordarea funcționează deoarece Ipoteza este construită pe Claude Code, cunoscând domeniul testării.
# test_properties.py
# Property-based testing con Hypothesis per codice AI
import pytest
from hypothesis import given, strategies as st, settings, assume
from hypothesis import HealthCheck
from decimal import Decimal
# Funzione AI-generated: calcolo sconto
# def calculate_discount(price: float, discount_percent: float) -> float:
# if discount_percent < 0 or discount_percent > 100:
# raise ValueError("Discount must be between 0 and 100")
# return price * (1 - discount_percent / 100)
from pricing import calculate_discount
# Proprietà 1: idempotenza - sconto 0% non cambia il prezzo
@given(price=st.floats(min_value=0.01, max_value=1_000_000.0, allow_nan=False))
def test_zero_discount_preserves_price(price):
"""Sconto zero deve restituire il prezzo originale."""
result = calculate_discount(price, 0.0)
assert abs(result - price) < 0.01 # tolleranza floating point
# Proprietà 2: monotonia - sconto maggiore = prezzo minore
@given(
price=st.floats(min_value=1.0, max_value=10000.0, allow_nan=False),
discount1=st.floats(min_value=0.0, max_value=50.0, allow_nan=False),
discount2=st.floats(min_value=50.0, max_value=100.0, allow_nan=False),
)
def test_higher_discount_lower_price(price, discount1, discount2):
"""Sconto maggiore deve produrre prezzo inferiore o uguale."""
result1 = calculate_discount(price, discount1)
result2 = calculate_discount(price, discount2)
assert result1 >= result2
# Proprietà 3: range output - prezzo finale sempre tra 0 e prezzo originale
@given(
price=st.floats(min_value=0.0, max_value=1_000_000.0, allow_nan=False),
discount=st.floats(min_value=0.0, max_value=100.0, allow_nan=False),
)
def test_output_range(price, discount):
"""Il prezzo scontato deve essere nel range [0, price]."""
result = calculate_discount(price, discount)
assert 0.0 <= result <= price + 0.01 # piccola tolleranza FP
# Proprietà 4: boundary - sconto 100% deve azzerare il prezzo
@given(price=st.floats(min_value=0.01, max_value=1_000_000.0, allow_nan=False))
def test_full_discount_zero_price(price):
"""Sconto del 100% deve risultare in prezzo zero."""
result = calculate_discount(price, 100.0)
assert abs(result) < 0.01
# Proprietà 5: validazione input - discount fuori range solleva eccezione
@given(discount=st.one_of(
st.floats(max_value=-0.001, allow_nan=False),
st.floats(min_value=100.001, allow_nan=False, allow_infinity=False)
))
def test_invalid_discount_raises(discount):
"""Discount fuori range [0, 100] deve sollevare ValueError."""
assume(not (discount != discount)) # esclude NaN
with pytest.raises(ValueError):
calculate_discount(100.0, discount)
# Test con st.composite per dati correlati
@st.composite
def valid_order(draw):
"""Strategia composita per ordini validi."""
price = draw(st.floats(min_value=1.0, max_value=10000.0, allow_nan=False))
discount = draw(st.floats(min_value=0.0, max_value=99.9, allow_nan=False))
return {"price": price, "discount": discount}
@given(order=valid_order())
@settings(max_examples=500, suppress_health_check=[HealthCheck.too_slow])
def test_order_processing_invariants(order):
"""Invarianti su ordini casuali generati da Hypothesis."""
result = calculate_discount(order["price"], order["discount"])
# Il risultato deve essere un numero finito
assert result == result # esclude NaN
assert result < float('inf')
# Il risultato deve essere non negativo
assert result >= 0.0
Analiză statică: ESLint, SonarQube și reguli personalizate
Analiza statică este primul strat de apărare împotriva codului AI de calitate scăzută. Se realizează fără a fi nevoie să ruleze codul, iar din acest motiv este ideal să se integreze în bucla de comitere sau conducta CI/CD ca o poartă obligatorie înainte de oricare disloca.
Pentru codul AI, configurația standard a instrumentelor de analiză statică nu
suficient. Codul AI tinde să producă modele specifice care necesită reguli dedicate:
funcții prea lungi generate într-un singur bloc, dependențe circulare, utilizare excesivă
de any în TypeScript, importuri de module inexistente sau depreciate.
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"ecmaVersion": 2024
},
"plugins": ["@typescript-eslint", "security", "sonarjs"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/strict",
"plugin:security/recommended",
"plugin:sonarjs/recommended"
],
"rules": {
// Regole critiche per codice AI-generated
// Proibisce 'any' esplicito - l'AI lo usa spesso per "sicurezza"
"@typescript-eslint/no-explicit-any": "error",
// Richiede return type esplicito - l'AI spesso lo omette
"@typescript-eslint/explicit-function-return-type": ["error", {
"allowExpressions": false,
"allowTypedFunctionExpressions": true
}],
// Cognitive complexity: l'AI genera funzioni lunghe e complesse
"sonarjs/cognitive-complexity": ["error", 15],
// Duplicazione: l'AI replica pattern identici in funzioni diverse
"sonarjs/no-duplicate-string": ["warn", 3],
"sonarjs/no-identical-functions": "error",
// Security: pattern comuni nel codice AI vulnerabile
"security/detect-object-injection": "error",
"security/detect-non-literal-regexp": "error",
"security/detect-possible-timing-attacks": "error",
"security/detect-unsafe-regex": "error",
// Complessità funzione - l'AI genera monoliti
"max-lines-per-function": ["warn", {
"max": 50,
"skipBlankLines": true,
"skipComments": true
}],
// Nesting profondo - pattern frequente nel codice AI
"max-depth": ["warn", 4],
// Promise handling - l'AI dimentica spesso await
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
// Null safety
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/strict-null-checks": "error",
// Import di moduli non utilizzati (hallucinated imports)
"@typescript-eslint/no-unused-vars": "error",
"no-unused-vars": "off" // sostituito da typescript version
},
"overrides": [
{
// Configurazione più stringente per codice generato da AI
"files": ["**/generated/**/*.ts", "**/ai-output/**/*.ts"],
"rules": {
"sonarjs/cognitive-complexity": ["error", 10],
"max-lines-per-function": ["error", {"max": 30}]
}
}
]
}
Pentru SonarQube, pe lângă configurația standard, este util să configurați a Quality Gate specifică codului AI cu praguri mai stricte: fără probleme de blocare, acoperire minimă de 80%, duplicare maximă de 3%, și o limită explicită a complexității ciclomatice a funcțiilor.
Testare de securitate: SAST cu Semgrep pentru cod AI
SAST specializat (Static Application Security Testing) pentru codul AI merge mai departe listing stil: caută modele de vulnerabilitate active, adesea invizibile pentru a revizuire manuală deoarece sunt corecte din punct de vedere sintactic. Semgrep este instrumentul de referință pentru această categorie în 2025, cu peste 20.000 de reguli predefinite și capacitatea de a scrie reguli personalizate care se potrivesc exact cu tiparele pe care AI le introduce.
În noiembrie 2025, Semgrep a anunțat versiunea beta privată a detectării sale bazate pe inteligență artificială sistem, care combină analiza statică tradițională cu raționamentul contextual pentru a reduce false pozitive cu 91% în comparație cu SAST-urile independente. Pentru codul AI, această abordare hibrid și deosebit de eficient pentru că înțelege contextul în care apare un model folosit, nu doar prezența acestuia.
# semgrep-ai-patterns.yaml
# Regole custom Semgrep per pattern di vulnerabilità tipici del codice AI-generated
rules:
# Regola 1: SQL injection via f-string o concatenazione
- id: ai-sql-injection-fstring
patterns:
- pattern: |
cursor.execute(f"...{$VAR}...")
- pattern: |
cursor.execute("..." + $VAR + "...")
message: |
Potenziale SQL injection via interpolazione diretta.
L'AI genera spesso query con f-string invece di parametri.
Usa cursor.execute("SELECT ... WHERE id = %s", (var,))
languages: [python]
severity: ERROR
metadata:
category: security
owasp: "A03:2021 - Injection"
ai-pattern: true
# Regola 2: Path traversal in file operations
- id: ai-path-traversal
patterns:
- pattern: |
open($BASE + $USER_INPUT, ...)
- pattern: |
open(os.path.join($BASE, $USER_INPUT), ...)
message: |
Potenziale path traversal. L'AI spesso concatena path utente
senza sanitizzazione. Usa pathlib.Path().resolve() e verifica
che il path risultante sia dentro la directory consentita.
languages: [python]
severity: ERROR
# Regola 3: Hardcoded credentials (pattern AI molto comune)
- id: ai-hardcoded-credentials
patterns:
- pattern: |
password = "$VALUE"
- pattern: |
api_key = "$VALUE"
- pattern: |
secret = "$VALUE"
- pattern: |
token = "$VALUE"
message: |
Credenziali hardcoded rilevate. L'AI inserisce spesso
placeholder realistici che sembrano credenziali reali.
Usa variabili d'ambiente o un secret manager.
languages: [python, javascript, typescript]
severity: ERROR
# Regola 4: Eval su input esterno
- id: ai-eval-injection
patterns:
- pattern: eval($USER_INPUT)
- pattern: exec($USER_INPUT)
message: |
eval/exec su input controllabile dall'utente. Pattern pericoloso
che l'AI introduce quando genera interpreti o sistemi dinamici.
languages: [python, javascript]
severity: ERROR
# Regola 5: Deserializzazione non sicura (Python pickle)
- id: ai-unsafe-deserialization
patterns:
- pattern: pickle.loads($DATA)
- pattern: pickle.load($FILE)
message: |
pickle.loads/load su dati non fidati. L'AI usa pickle
per semplicità senza considerare le implicazioni di sicurezza.
Usa json.loads() o librerie sicure come marshmallow.
languages: [python]
severity: ERROR
# Regola 6: JWT senza verifica firma
- id: ai-jwt-no-verification
patterns:
- pattern: |
jwt.decode($TOKEN, options={"verify_signature": False})
message: |
JWT decodificato senza verifica firma. Pattern frequente
nel codice AI generato per ambienti di sviluppo che poi
finisce in produzione.
languages: [python]
severity: ERROR
# Regola 7: Regex potenzialmente catastrofica (ReDoS)
- id: ai-catastrophic-regex
pattern-regex: '\(\.\*\)+|\(\.\+\)+'
message: |
Pattern regex potenzialmente vulnerabile a ReDoS.
L'AI genera spesso regex con backtracking esponenziale.
languages: [python, javascript, typescript]
severity: WARNING
#!/bin/bash
# scan-ai-code.sh
# Script per security scan di codice generato da AI
set -euo pipefail
AI_CODE_DIR="{{$1:-src}}"
RULES_FILE="semgrep-ai-patterns.yaml"
REPORT_FILE="security-report.json"
echo "Avvio security scan su: $AI_CODE_DIR"
# Scan con regole custom AI + ruleset OWASP
semgrep scan \
--config "$RULES_FILE" \
--config "p/owasp-top-ten" \
--config "p/python" \
--json \
--output "$REPORT_FILE" \
"$AI_CODE_DIR"
# Analizza risultati
ERRORS=$(jq '.results | map(select(.extra.severity == "ERROR")) | length' "$REPORT_FILE")
WARNINGS=$(jq '.results | map(select(.extra.severity == "WARNING")) | length' "$REPORT_FILE")
echo "Risultati: $ERRORS error(s), $WARNINGS warning(s)"
# Fail se ci sono error (non warning)
if [ "$ERRORS" -gt 0 ]; then
echo "FAIL: $ERRORS vulnerabilità critiche rilevate nel codice AI"
jq '.results[] | select(.extra.severity == "ERROR") | {file: .path, line: .start.line, message: .extra.message}' "$REPORT_FILE"
exit 1
fi
echo "Security scan completato con successo"
Date critice: Veracode 2025
Raportul Veracode 2025 a analizat peste 100 de LLM în 80 de sarcini de codare. Java este limbajul cel mai expus riscului Rata de eșec de securitate de 72%.. Python, C# și JavaScript sunt între 38% și 45%. Cel mai alarmant lucru: modelele mai noi și mai avansate nu sunt mai sigure decât cele mai vechi. Securitate a codului AI nu se îmbunătățește cu dimensiunea modelului.
TDD cu AI Assistant: Red-AI-Green-Review
Dezvoltarea tradițională bazată pe teste urmează ciclul Red-Green-Refactor: tu scrii testul (care eșuează), implementezi minimul necesar pentru a-l face să treacă, refactorezi. Cu un asistent AI, acest ciclu se transformă în Red-AI-Green-Review: scrii mai întâi testele (menținând controlul uman asupra specificațiilor), delegi implementarea se face la AI, toate testele sunt verificate pentru a trece și sunt efectuate revizuiri de cod concentrat pe modele de securitate și arhitectură.
Această abordare are un avantaj cheie: teste scrise de dezvoltator ele reprezintă specificațiile reale ale sistemului, nu înțelegerea de către AI a promptului. Dacă AI generează cod care eșuează testele, problema este explicită și măsurabilă. Dacă codul trece toate testele, dar are probleme de securitate, Semgrep le găsește. Dacă are defecte de design, revizuirea codului le identifică. Ciclul este complet.
# Passo 1: Lo sviluppatore scrive i test PRIMA
# test_payment_processor.py
import pytest
from decimal import Decimal
class TestPaymentProcessor:
"""
Specifiche per PaymentProcessor scritte PRIMA
di chiedere all'AI di implementarlo.
"""
def test_process_valid_payment(self, processor):
"""Pagamento valido deve restituire transaction_id."""
result = processor.process(
amount=Decimal("99.99"),
currency="EUR",
card_token="tok_valid_test"
)
assert result.success is True
assert result.transaction_id is not None
assert len(result.transaction_id) == 36 # UUID format
def test_negative_amount_rejected(self, processor):
"""Importo negativo deve sollevare ValueError."""
with pytest.raises(ValueError, match="Amount must be positive"):
processor.process(
amount=Decimal("-10.00"),
currency="EUR",
card_token="tok_valid_test"
)
def test_unsupported_currency_rejected(self, processor):
"""Valuta non supportata deve sollevare ValueError."""
with pytest.raises(ValueError, match="Currency not supported"):
processor.process(
amount=Decimal("50.00"),
currency="XYZ",
card_token="tok_valid_test"
)
def test_invalid_token_raises_payment_error(self, processor):
"""Token non valido deve sollevare PaymentError, non crashare."""
with pytest.raises(PaymentError, match="Invalid card token"):
processor.process(
amount=Decimal("50.00"),
currency="EUR",
card_token=""
)
def test_duplicate_idempotency(self, processor):
"""Stesso idempotency_key non deve creare duplicati."""
result1 = processor.process(
amount=Decimal("25.00"),
currency="EUR",
card_token="tok_valid_test",
idempotency_key="key-123"
)
result2 = processor.process(
amount=Decimal("25.00"),
currency="EUR",
card_token="tok_valid_test",
idempotency_key="key-123"
)
assert result1.transaction_id == result2.transaction_id
def test_amount_precision_preserved(self, processor):
"""Precisione decimale deve essere mantenuta."""
result = processor.process(
amount=Decimal("0.01"),
currency="EUR",
card_token="tok_valid_test"
)
assert result.charged_amount == Decimal("0.01")
# Passo 2: Si chiede ad AI di implementare PaymentProcessor
# Prompt: "Implementa PaymentProcessor che passa tutti questi test.
# Usa Decimal per gli importi, valida input,
# gestisci l'idempotency, non usare float."
# Passo 3: Si verifica che l'implementazione AI passi tutti i test
# pytest test_payment_processor.py -v
# Passo 4: Si esegue Semgrep sull'implementazione
# semgrep --config semgrep-ai-patterns.yaml payment_processor.py
# Passo 5: Code review focalizzata su sicurezza e architettura
Revizuire automată a codului: conductă cu mai mulți agenți
O conductă de revizuire a codului cu mai mulți agenți pentru codul AI combină mai mulți specialiști care operează în paralel pe diferite aspecte ale calității: un agent de securitate recenzie, una despre stilul de cod, una despre performanță, una despre arhitectură. Rezultatul și un raport structurat pe care dezvoltatorul îl folosește ca punct de plecare pentru revizuire umană, nu ca înlocuitor.
Abordarea nu este nouă (există instrumente precum CodeClimate, DeepSource, Codacy de ani de zile), dar în 2025 calitatea modelelor a făcut o conductă viabilă complet agentic. Claude Code, de exemplu, poate fi orchestrat în Subagenți paraleli care analizează codul din diferite unghiuri și agreg rezultatele într-un singur raport de revizuire.
# ai_code_review_pipeline.py
# Pipeline multi-agent per review di codice AI-generated
import asyncio
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import List
@dataclass
class ReviewResult:
agent: str
severity: str # "critical", "high", "medium", "low", "info"
category: str
message: str
file: str
line: int | None = None
async def run_security_agent(file_path: str) -> List[ReviewResult]:
"""Agente security: esegue Semgrep + bandit."""
results = []
# Semgrep scan
proc = await asyncio.create_subprocess_exec(
"semgrep", "--config", "p/owasp-top-ten",
"--json", file_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, _ = await proc.communicate()
import json
data = json.loads(stdout)
for finding in data.get("results", []):
results.append(ReviewResult(
agent="security",
severity="critical" if finding["extra"]["severity"] == "ERROR" else "high",
category="security",
message=finding["extra"]["message"],
file=finding["path"],
line=finding["start"]["line"]
))
return results
async def run_complexity_agent(file_path: str) -> List[ReviewResult]:
"""Agente complessità: analizza cyclomatic complexity."""
results = []
proc = await asyncio.create_subprocess_exec(
"radon", "cc", "-s", "-n", "B", file_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, _ = await proc.communicate()
for line in stdout.decode().splitlines():
if line.strip():
# radon output: " M 15:4 method_name - B (7)"
parts = line.strip().split()
if len(parts) >= 5:
results.append(ReviewResult(
agent="complexity",
severity="medium" if "B" in parts else "high",
category="maintainability",
message=f"Funzione con alta complessità: {' '.join(parts)}",
file=file_path
))
return results
async def run_coverage_agent(test_file: str, source_file: str) -> List[ReviewResult]:
"""Agente coverage: verifica che i test coprano il codice AI."""
results = []
proc = await asyncio.create_subprocess_exec(
"pytest", test_file,
f"--cov={source_file}",
"--cov-report=json:coverage.json",
"--quiet",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
await proc.communicate()
import json
try:
with open("coverage.json") as f:
cov_data = json.load(f)
total = cov_data["totals"]["percent_covered"]
if total < 80.0:
results.append(ReviewResult(
agent="coverage",
severity="high",
category="testing",
message=f"Coverage insufficiente: {total:.1f}% (minimo 80%)",
file=source_file
))
except FileNotFoundError:
results.append(ReviewResult(
agent="coverage",
severity="critical",
category="testing",
message="Impossibile calcolare la coverage. Test mancanti?",
file=source_file
))
return results
async def review_ai_code(
source_file: str,
test_file: str | None = None
) -> dict:
"""Pipeline completa di review per codice AI-generated."""
tasks = [
run_security_agent(source_file),
run_complexity_agent(source_file),
]
if test_file:
tasks.append(run_coverage_agent(test_file, source_file))
# Esecuzione parallela di tutti gli agenti
all_results = await asyncio.gather(*tasks, return_exceptions=True)
findings: List[ReviewResult] = []
for result in all_results:
if isinstance(result, Exception):
print(f"Agente fallito: {result}")
else:
findings.extend(result)
# Aggregazione risultati
critical = [f for f in findings if f.severity == "critical"]
high = [f for f in findings if f.severity == "high"]
return {
"total_findings": len(findings),
"critical": len(critical),
"high": len(high),
"approve": len(critical) == 0 and len(high) == 0,
"findings": findings
}
# Uso
if __name__ == "__main__":
result = asyncio.run(review_ai_code(
source_file="payment_processor.py",
test_file="test_payment_processor.py"
))
print(f"Review completata: {result['total_findings']} findings")
print(f"Approvato: {result['approve']}")
if not result["approve"]:
print("\nISSUE CRITICHE:")
for f in result["findings"]:
if f.severity in ["critical", "high"]:
print(f" [{f.severity.upper()}] {f.file}:{f.line} - {f.message}")
Valori de calitate pentru codul AI
Testarea codului AI necesită un set mai larg de valori decât o simplă acoperire a codului. Acoperirea măsoară câte linii de cod execută testele, dar nu măsoară calitatea dintre acele teste. Un cod AI poate avea o acoperire de 90% cu teste care nu verifică nimic relevante. Valorile suplimentare necesare sunt:
Scorul de mutație
Testarea mutațiilor este cea mai fiabilă modalitate de a măsura calitatea testelor și nu numai
cantitatea lor. Un tester de mutații (cum ar fi mutmut pentru Python sau
Stryker pentru JavaScript/TypeScript) introduce sistematic mic
modificări ale codului (mutanți: schimbare > in >=, revers a
boolean, elimină un return) și verifică dacă testele existente detectează aceste modificări.
Dacă un mutant supraviețuiește testelor, testele nu sunt suficient de granulare.
Pentru codul AI, scorul minim de mutație țintă este 70% (nivelul de aur). Scorurile sub 50% indică o suită de teste inadecvată, indiferent acoperire procentuală.
# setup.cfg - Configurazione mutmut
[mutmut]
paths_to_mutate=src/
backup=False
runner=python -m pytest tests/ -x -q
tests_dir=tests/
dict_synonyms=
# Escludi file di configurazione e init
exclude=setup.py,conftest.py
# Esegui mutation testing
# mutmut run
# Visualizza risultati
# mutmut results
# Esempio di output:
# Mutant #1: SURVIVED
# src/payment_processor.py:45
# - if amount <= 0:
# + if amount < 0:
#
# Indica che il test per amount=0 non esiste!
# Aggiungere: assert raises ValueError for amount=0
# Script per CI/CD con soglia minima
#!/bin/bash
mutmut run --no-progress
KILLED=$(mutmut results | grep "Killed" | grep -o '[0-9]*' | head -1)
TOTAL=$(mutmut results | grep "Total" | grep -o '[0-9]*' | head -1)
if [ -z "$TOTAL" ] || [ "$TOTAL" -eq 0 ]; then
echo "Nessun mutante generato"
exit 0
fi
SCORE=$(echo "scale=2; $KILLED * 100 / $TOTAL" | bc)
echo "Mutation score: $SCORE%"
# Soglia minima 70% per codice AI
if (( $(echo "$SCORE < 70" | bc -l) )); then
echo "FAIL: Mutation score insufficiente per codice AI ($SCORE% < 70%)"
exit 1
fi
echo "OK: Mutation score accettabile ($SCORE%)"
Complexitatea ciclomatică
Complexitatea ciclomatică măsoară numărul de căi liniar independente printr-o functie. O funcție cu complexitatea 1 are o singură cale posibilă (fără ramuri). O funcție cu complexitatea 20 are 20 de căi și necesită cel puțin 20 de cazuri de testare pentru acoperirea completă a ramurilor. Codul AI tinde să se genereze funcţionează cu o complexitate ridicată deoarece încearcă să gestioneze toate cazurile într-o singură funcţie monolitic.
Țintele recomandate pentru codul AI în 2025 sunt: valoarea mediană < 10 dupa functie, steag automat > 15 pentru revizuire obligatorie, de ex respingere automată > 25 ca poarta CI/CD. Aceste valori sunt mai stricte a codului uman, deoarece codul AI este foarte complex și mai puțin previzibil tratarea erorilor cu privire la codul complex scris de un dezvoltator.
Cele mai bune practici: Lista de verificare pentru acceptarea codului AI
Următoarea listă de verificare reprezintă poarta de calitate minimă înainte de integrare Cod generat de AI într-o bază de cod de producție. Nu este o listă exhaustivă, dar acoperă cele mai frecvente categorii de erori documentate în 2025.
Lista de verificare: Poarta de calitate pentru codul generat de AI
Nivelul 1 - Automatizat (Poarta CI/CD, blocant)
- Toate testele unitare trec (0 eșec)
- Scanare de securitate Semgrep: 0 problemă de gravitate EROARE
- ESLint/TypeScript: 0 erori (avertismente acceptabile cu justificare)
- Acoperire cod >= 80% pe linii noi
- Fără dependențe cu CVE cunoscut (audit npm/pip)
- Fără acreditări hardcoded (git-secrets, detect-secrets)
Stratul 2 - Valori de calitate (avertisment dacă nu sunt respectate)
- Scorul de mutație >= 70% la modulele critice
- Complexitatea ciclomatică < 15 pentru fiecare funcție
- Complexitatea cognitivă < 20 pentru fiecare funcție
- Nu există funcții cu mai mult de 50 de linii
- Dublarea codului < 3%
Nivelul 3 - Examinare manuală (obligatoriu pentru modificări semnificative)
- Logica de afaceri verificată în raport cu specificațiile originale
- Gestionarea erorilor revizuită: fără captură silențioasă, fără excepție generică
- Cazuri marginale documentate în teste: intrare goală, valori nule, extreme
- Fără apeluri către API-uri externe fără expirări și reîncercați logica
- Mutație de stare izolată: fără efecte secundare nedocumentate
- Teste bazate pe proprietăți pentru funcții cu introducere numerică sau textuală
Nivelul 4 - Revizuire arhitecturală (pentru module noi sau refactorizări semnificative)
- Responsabilitatea unică respectată (un motiv pentru schimbare per clasă)
- Dependențe de abstracții, nu implementări concrete
- Fără dependențe circulare între module
- Interfețe publice stabile și bine documentate
Anti-Pattern: Ce NU trebuie să faceți cu codul AI
- Nu îmbinați niciodată fără a rula teste: chiar și pentru „micile corecturi”. Codul AI este determinist, dar nu transparent: o modificare a promptului se schimbă întreaga implementare în moduri neevidente.
- Nu aveți încredere niciodată că „funcționează pe computerul meu”: codul AI și deosebit de sensibile la diferențele de mediu. Testarea CI/CD pe un container curatenia sunt obligatorii.
- Nu acceptați niciodată codul AI pe formularele de autentificare fără SAST: autentificarea și autorizarea sunt zonele în care codul AI este cel mai periculos din punct de vedere statistic. Raportul Veracode citează JWT fără verificare, gestionarea incorectă a sesiunii și privilegii escaladare printre primele 5 vulnerabilități AI.
- Nu utilizați AI pentru a genera teste pentru codul AI: testele trebuie să fie scris de dezvoltator. Dacă cereți AI-ului să scrie teste pentru el cod, veți obține teste care verifică implementarea AI, nu specificul sistemului.
Concluzii: testarea ca un activator al codării Vibe
Scopul acestui articol nu este de a speria oamenii care folosesc AI pentru a scrie cod. Și invers: construirea încrederii pentru a-l folosi bine. 45% securitate rata de eșec a codului AI nu înseamnă că 45% din codul AI care vine producție și nesigure. Înseamnă că fără un proces structurat de verificare, asta este probabilitatea. Cu procesul corect, acest procent scade drastic.
Combinația de TDD (teste scrise înainte, nu după), testare bazată pe proprietăți cu Ipoteza, SAST cu Semgrep, analiză statică cu ESLint configurat pentru modele AI, și testarea mutațiilor pentru a verifica calitatea testelor, creează un sistem de protecție ceea ce face ca codificarea vibrațiilor să fie profesionistă. Nu încetinește dezvoltarea: o stabilizează.
Următorul articol din serie explorează Prompt Engineering pentru IDE e Generarea codului: cum să scrieți solicitări care produc un cod mai bun, mai sigur, mai ușor de întreținut, reducând munca de testare în aval.
Resurse și link-uri utile
Seria Vibe Coding și Dezvoltare Agentică
Toată seria
- Vibe Coding: Paradigma care s-a schimbat în 2025
- Claude Code: Dezvoltare Agentică Terminală
- Fluxuri de lucru agentice: probleme de descompunere pentru AI
- Codare multi-agent: LangGraph, CrewAI și AutoGen
- Testarea codului generat de AI (acest articol)
- Prompt Engineering pentru IDE și generare de cod
- Securitate în codarea Vibe: riscuri și atenuări
- Viitorul dezvoltării agentice în 2026
Perspective aferente
- Cursor IDE: Primul AI-First IDE - Cum se ocupă Cursor de testarea codului AI
- OWASP Top 10 2025: Vulnerabilități curente - Contextul vulnerabilităților pe care AI le introduce
- Claude pentru Code Review - Folosește-l pe Claude ca agent de recenzie







