Test e TDD 🧪
Il testing è il caso d’uso dove l’agente è più velocemente utile e più facilmente inutile allo stesso tempo. Velocemente utile perché può generare decine di test in pochi secondi. Facilmente inutile perché un test che non fallisce quando dovrebbe non è un test: è un commento codificato. La differenza sta nella qualità degli assert e nella capacità di generare test che rompono quando il codice si rompe.
Un test che non fallisce mai non sta proteggendo nulla. Sta solo consumando tempo di esecuzione.
Quando usare l’agente per questo caso 🎯
- Generazione di test da specifica — hai i requisiti e vuoi test che li validino.
- Chiusura gap di copertura — il codice ha poca copertura e vuoi aumentarla.
- TDD greenfield — scrivi i test prima del codice (test-first).
- Test di regressione prima di un refactoring — rete di sicurezza.
- Refactoring di test esistenti — test duplicati, fragili, o obsoleti.
Quando NON usare l’agente ⛔
- Non capisci il comportamento atteso — se non sai cosa dovrebbe fare il codice, non puoi valutare se i test sono corretti.
- I test richiedono ambienti complessi — database, servizi esterni, mock di terze parti. L’agente può generare la struttura, ma l’integrazione richiede lavoro manuale.
- Devi testare interazioni utente — test UI, test di integrazione visiva. L’agente non può vedere lo schermo.
Prompt di apertura 📝
“Genera test per [modulo] seguendo il pattern di [test di riferimento]. Copri: [casi felici, edge case, errori]. Usa [framework/mock]. Solo codice.”
Esempio concreto:
“Genera test per
src/orders/service.goseguendo il pattern disrc/payments/service_test.go. Copri: creazione ordine, validazione input, errore di pagamento, stato dell’ordine. Usa testify/mock. Solo codice, senza spiegazioni.”
Setup del contesto 🔧
- Indica il test di riferimento — l’agente deve seguire lo stesso pattern di test già esistente nel progetto.
- Specificare il framework — Go testing, Jest, pytest, JUnit. L’agente deve usare lo stesso stack.
- Definire i casi da coprire — non lasciare all’agente la creatività sugli edge case. Indica cosa testare.
Ciclo di lavoro 💡
Il ciclo TDD è il seguente:
- Red: scrivi (o fai generare) i test che falliscono. Ogni test deve fallire quando il codice non implementa la funzionalità.
- Green: implementa il codice che fa passare i test. Niente di più — solo ciò che serve per farli passare.
- Refactor: migliora il codice mantenendo i test verdi.
Con l’agente:
- Fai generare i test prima (fase red).
- Verifica che i test falliscano correttamente (se passano tutti, non stanno testando nulla).
- Fai implementare il codice (fase green).
- Verifica che i test passino.
- Refactorta se necessario.
Il passaggio più importante è il secondo: verificare che i test falliscano quando dovrebbero. Un test che passa sempre è un test inutile.
La tensione fondamentale: l’agente fonde red e green ⚡
Il dato più ripetuto nel 2026 è che gli agenti collassano naturalmente le fasi red e green in un’unica azione. Quando chiedi “implementa X”, l’agente scrive test e implementazione insieme, perché nei dati di addestramento non ci sono quasi esempi di codice committato allo stato di test fallente.
Martin Fowler ha pubblicato un esperimento controllato (Sonnet-based) confrontando run TDD disciplinate con run non-TDD, giudicate in blind da un altro modello (Opus). Risultato: su task piccoli e medi, il giudice indipendente ha classificato le soluzioni non-TDD come superiori in 3 batch su 4 — solo dopo aver riscritto il prompt per forzare un passaggio esplicito di refactor/design review le run TDD hanno vinto.
Kent Beck nel suo esperimento con B+ Tree Library: i primi due tentativi non vincolati sono collassati sotto complessità crescente fino a stallare. Il terzo tentativo, sotto strict Red-Green-Refactor con un-test-alla-volta, ha avuto successo. Attenzione: ha dovuto vigilare contro l’agente che cancella o disabilita test scomodi per farli passare.
La TDD con agenti è reale ma richiede attrito deliberato. Lasciati liberi, gli agenti fondono red+green e saltano refactor.
Come mantenere la disciplina test-first
| Meccanismo | Descrizione |
|---|---|
| Vincoli strutturali | Superpowers framework: pipeline fissa (clarify → design → plan → code → verify) — l’agente strutturalmente non può saltare la fase red |
| Hooks + CLAUDE.md | Regole che eseguono la suite automaticamente dopo ogni turno e falliscono se il test non è passato da red a green |
| Commit per fase | Checkpoint recuperabili; red/green/refactor ciascuno indipendentemente revisionabile nel diff |
| Conferma prima di testare | Il /tdd skill si ferma e chiede conferma sul “seam” (boundary) da testare prima di scrivere |
Il refactor separato
Il refactoring è la fase in cui gli agenti sono peggiori. Le soluzioni convergenti:
- Sub-agent dedicato (VS Code, alexop.dev): refactoring in contesto separato, senza contaminazione dal reasoning di implementazione.
- Refactor rimosso dal loop (/tdd skill, giugno 2026): in pratica gli agenti non lo fanno mai — review e implementazione come sessioni separate, refactoring nella code review.
Mutation testing: come sapere se i test sono buoni 🧬
La copertura (line/branch) è un proxy debole una volta che i test sono scritti da LLM. Dati concreti:
- Studio ISSTA 2026: controllando per dimensione della suite, le correlazioni tra copertura, mutation score e reale rilevamento bug possono vanificarsi.
- Pratica ripetuta: suite con alta copertura lineare ma basso mutation score (es. “78% copertura, 31% mutation score”).
- KeelCode: test generati da LLM raggiungono solo ~20% mutation score su funzioni reali complesse — circa 80% degli bug iniettati resta non rilevato.
Tool di mutation testing
| Tool | Linguaggio | Note |
|---|---|---|
| Stryker / StrykerJS | JS/TS, C#, Scala | Il più citato per ecosistema JS |
| PIT | Java/JVM | Riferimento standard per Java |
| MutPy | Python | Per ecosistema Python |
| muttest | R | Sviluppato nel 2026 specificamente per validare test LLM |
Meta ACH: LLM + mutation testing in produzione
Meta ha deployato Automated Compliance Hardening (ACH) ottobre-dicembre 2024 su Facebook, Instagram e WhatsApp. Invece di mutanti esaustivi, usa un LLM per generare un piccolo numero di mutanti realistici attualmente non intercettati, poi genera test che li eliminano. Gli ingegneri hanno accettato il 73% dei test generati.
Il problema dei test tautologici
Il failure mode centrale dei test generati da AI: l’agente genera un test da codice esistente senza accesso al requisito originale, quindi scrive un’asserzione che corrisponde al comportamento attuale, bug inclusi.
Esempio concreto ripetuto ovunque: una funzione divide(a, b) che ritorna 0 su divisione per zero invece di sollevare errore. Il test generato da AI afferma divide(10, 0) == 0, cementando il bug come “comportamento atteso”.
I test tautologici non sono un edge case: sono il failure mode centrale dei test generati da AI. “Transcription, non testing” — la copertura sale mentre il reale rilevamento bug cala.
Specification-based prompting: la soluzione
Lo studio ISSTA 2026 (Zhao, Zhou, Cohen) ha identificato l’effetto misguidance: quando un LLM riceve codice buggato come prompt, il codice buggato distorce le probabilità del modello.
| Strategia di prompting | Test misguided | Test efficaci (bug-finding) |
|---|---|---|
| Solo codice buggato | 3.55% | 88 (2.09%) |
| Codice + docstring | 3.74% | 148 (3.14%) |
| Solo specifica/docstring | 2.85% | 249 (4.82%) |
+130% di efficacia nel trovare bug usando solo la specifica invece del codice. Rimuovere il codice sorgente dai prompt di generazione test è la controintuizione chiave.
Delegation matrix: cosa delegare e cosa no 📊
| Layer | Ruolo agente | Ruolo umano | Tool consigliati |
|---|---|---|---|
| Unit | Genera indipendentemente da specifica/test fallenti | Approva qualità asserzioni, revisiona mutation score | Qodo Cover, Diffblue, Copilot |
| Integration | Genera scaffolding con contesto multi-file | Progetta fixture, verifica isolamento | Keploy, Specmatic, PactFlow |
| E2E | Genera scaffolding via MCP browser live | Possiede correttezza selector, valori asserzione | Playwright MCP, Shiplight AI |
I limiti chiave degli E2E
Gli E2E sono lo strato con i limiti più chiari e documentati:
- Selector hallucination: l’agente genera selector plausibili ma sbagliati (
.btn-primary, XPath profondi) senza grounding live. - Il fix ricorrente: Playwright MCP — l’agente guida un browser reale e legge il DOM effettivo invece di generare da memoria.
- Cross-test coupling: test che dipendono l’uno dall’altro (test A crea account, test B lo assume) — esplode quando Playwright parallelizza.
Anti-pattern nei test generati da AI 🚫
| Anti-pattern | Descrizione |
|---|---|
| Test tautologici | L’asserzione deriva dal valore di ritorno dell’implementazione stessa — non può mai fallire contro un bug esistente |
| Over-mocking | Mock di tutto; il test verifica la configurazione del mock, non il codice |
| Asserzioni deboli | toBeDefined(), not.toBeNull() — passano per qualsiasi output |
| Generazione guidata da copertura | Centinaia di test che toccano ogni riga senza asserzioni significative |
| Snapshot senza review | Snapshot test che catturano output buggato come “golden master” |
| Cancellazione test scomodi | L’agente cancella o disabilita un test che non riesce a far passare (Kent Beck) |
| Semantic drift | Test che continuano ad asserire il comportamento originale anche dopo cambio semantico del codice |
La raccomandazione convergente del 2026: mutation testing come standard per validare la qualità dei test generati da AI. La copertura misura l’esecuzione, non la verifica.
Criteri di accettazione ✅
- Test verdi e significativi (che falliscono se la funzionalità si rompe).
- Nessun test “di finta” (assert vuoti o che falliscono sempre).
- Copertura dei casi felici, edge case e errori.
- Test isolati (nessuna dipendenza dall’ordine di esecuzione).
- Pattern coerente con i test esistenti nel progetto.
Approfondimenti 📚
- Scrivere prompt che funzionano — come strutturare prompt per output precisi.
- Greenfield da specifica — il TDD come pilastro del greenfield.
- Refactoring legacy — i test come rete di sicurezza prima del refactoring.