Scopri un prompt per la generazione di test con Claude Code che produce test ad alto segnale focalizzandosi su confini, invarianti e modalità di errore invece degli happy-path.

I suite di test generate automaticamente spesso sembrano impressionanti: decine di test, molto codice di setup e ogni nome di funzione appare da qualche parte. Ma molti di quei test sono solo controlli del tipo “funziona quando tutto è normale”. Passano facilmente, raramente trovano bug e comunque costano tempo per essere letti e mantenuti.
Con un tipico prompt per la generazione di test con Claude Code, il modello tende a rispecchiare gli input d'esempio che vede. Ottieni variazioni che sembrano diverse ma coprono lo stesso comportamento. Il risultato è una grande suite con copertura sottile dove conta davvero.
I test ad alto segnale sono diversi. Sono il piccolo insieme che avrebbe intercettato l'incidente del mese scorso. Falliscono quando il comportamento cambia in modo rischioso e restano stabili quando avvengono refactor innocui. Un test ad alto segnale può valere venti controlli “restituisce il valore atteso”.
La generazione di basso valore per gli happy-path di solito ha alcuni sintomi chiari:
Immagina una funzione che applica un codice sconto. I test happy-path confermano che “SAVE10” riduce il prezzo. I veri bug si nascondono altrove: prezzi 0 o negativi, codici scaduti, problemi di arrotondamento o limiti massimi di sconto. Sono quei casi che generano totali sbagliati, clienti arrabbiati e rollback a mezzanotte.
L'obiettivo è spostarsi da “più test” a “test migliori” mirando a tre bersagli: confini, modalità di errore e invarianti.
Se vuoi unit test ad alto segnale, smetti di chiedere “più test” e inizia a chiedere tre tipi specifici. Questo è il nucleo di un prompt per la generazione di test con Claude Code che produce copertura utile invece di una pila di controlli “funziona su input normale”.
I confini sono i margini di ciò che il codice accetta o produce. Molti difetti reali sono off-by-one, stati vuoti o problemi di timeout che non compaiono in un happy path.
Pensa in termini di minimi e massimi (0, 1, lunghezza massima), vuoto vs presente ("", [], nil), off-by-one (n-1, n, n+1) e limiti temporali (vicino alla soglia).
Esempio: se un'API accetta “fino a 100 elementi”, testa 100 e 101, non solo 3.
Le failure mode sono i modi in cui il sistema può rompersi: input errati, dipendenze mancanti, risultati parziali o errori a monte. Buoni test per le modalità di errore controllano il comportamento sotto stress, non solo l'output in condizioni ideali.
Esempio: quando una chiamata al database fallisce, la funzione restituisce un errore chiaro ed evita di scrivere dati parziali?
Le invarianti sono verità che dovrebbero restare vere prima e dopo una chiamata. Trasformano la correttezza vaga in asserzioni nette.
Esempi:
Quando ti concentri su questi tre obiettivi, ottieni meno test ma ognuno ha più segnale.
Se chiedi test troppo presto, di solito ottieni una pila di controlli “funziona come previsto”. Una semplice soluzione è scrivere prima un piccolo contratto, poi generare test da quel contratto. È il modo più veloce per trasformare un prompt di generazione di test con Claude Code in qualcosa che trova bug reali.
Un contratto utile è abbastanza breve da poter essere letto in un respiro. Mira a 5–10 righe che rispondano a tre domande: cosa entra, cosa esce e cos'altro cambia.
Scrivi il contratto in linguaggio semplice, non in codice, e includi solo ciò che puoi testare.
Una volta che hai questo, scansiona per capire dove la realtà può infrangere le assunzioni. Quelli diventano casi limite (min/max, zero, overflow, stringhe vuote, duplicati) e modalità di errore (timeout, permesso negato, violazioni di vincoli unici, input corrotti).
Ecco un esempio concreto per una feature come reserveInventory(itemId, qty):
Il contratto potrebbe dire che qty deve essere un intero positivo, la funzione deve essere atomica e non deve mai creare stock negativo. Questo suggerisce subito test ad alto segnale: qty = 0, qty = 1, qty maggiore della disponibilità, chiamate concorrenti e un errore forzato al DB a metà operazione.
Se usi uno strumento di tipo vibe-coding come Koder.ai, lo stesso workflow si applica: scrivi prima il contratto in chat, poi genera test che attacchino direttamente confini, modalità di errore e la lista “non deve mai succedere”.
Usa questo prompt per la generazione di test con Claude Code quando vuoi meno test ma ciascuno più efficace. La mossa chiave è imporre prima un piano di test, poi generare codice solo dopo che il piano è stato approvato.
You are helping me write HIGH-SIGNAL unit tests.
Context
- Language/framework: <fill in>
- Function/module under test: <name + short description>
- Inputs: <types, ranges, constraints>
- Outputs: <types + meaning>
- Side effects/external calls: <db, network, clock, randomness>
Contract (keep it small)
1) Preconditions: <what must be true>
2) Postconditions: <what must be true after>
3) Error behavior: <how failures are surfaced>
Task
PHASE 1 (plan only, no code):
A) Propose 6-10 tests max. Do not include “happy path” unless it protects an invariant.
B) For each test, state: intent, setup, input, expected result, and WHY it is high-signal.
C) Invariants: list 3-5 invariants and how each will be asserted.
D) Boundary matrix: propose a small matrix of boundary values (min/max/empty/null/off-by-one/too-long/invalid enum).
E) Failure modes: list negative tests that prove safe behavior (no crash, no partial write, clear error).
Stop after PHASE 1 and ask for approval.
PHASE 2 (after approval):
Generate the actual test code with clear names and minimal mocks.
Un trucco pratico è richiedere la matrice dei confini come tabella compatta, così i gap risultano evidenti:
| Dimension | Valid edge | Just outside | “Weird” value | Expected behavior |
|---|---|---|---|---|
| length | 0 | -1 | 10,000 | error vs clamp vs accept |
Se Claude propone 20 test, spingi indietro. Chiedi di unire casi simili e di tenere solo quelli che effettivamente catturerebbero un bug reale (off-by-one, tipo di errore sbagliato, perdita silente di dati, invariante rotta).
Inizia con un contratto piccolo e concreto per il comportamento che vuoi. Incolla la signature della funzione, una breve descrizione di input e output e eventuali test esistenti (anche se sono solo happy-path). Questo mantiene il modello ancorato a ciò che il codice fa realmente, non a ciò che suppone.
Poi chiedi una tabella dei rischi prima di chiedere qualsiasi codice di test. Richiedi tre colonne: casi limite (bordi dell'input valido), modalità di errore (input cattivi, dati mancanti, timeout) e invarianti (regole che devono sempre valere). Aggiungi una frase per riga: “perché questo può rompersi”. Una tabella semplice rivela gap più velocemente di una pila di file di test.
Quindi scegli il set più piccolo di test in cui ognuno ha uno scopo unico di cattura bug. Se due test falliscono per lo stesso motivo, tieni quello più forte.
Una regola pratica di selezione:
Infine, richiedi una breve spiegazione per test: quale bug catturerebbe se fallisse. Se la spiegazione è vaga (“valida il comportamento”), probabilmente il test è a basso segnale.
Un'invariante è una regola che dovrebbe restare vera qualunque input valido tu passi. Con il testing basato su invarianti, prima scrivi la regola in linguaggio semplice, poi la trasformi in un'asserzione che possa fallire in modo rumoroso.
Scegli 1 o 2 invarianti che ti proteggono davvero dai bug. Buone invarianti riguardano spesso la sicurezza (nessuna perdita di dati), la consistenza (stessi input, stessi output) o limiti (non superare mai certi capi).
Scrivi l'invariante come una breve frase, poi decidi quale evidenza il test può osservare: valori di ritorno, dati memorizzati, eventi emessi o chiamate alle dipendenze. Le asserzioni forti controllano sia l'esito sia gli effetti collaterali, perché molti bug si nascondono in “ha restituito OK ma ha scritto la cosa sbagliata”.
Per esempio, supponi di avere una funzione che applica un coupon a un ordine:
Ora codifica queste come asserzioni che misurano qualcosa di concreto:
expect(result.total).toBeGreaterThanOrEqual(0)
expect(db.getOrder(orderId).discountCents).toBe(originalDiscountCents)
Evita asserzioni vaghe come “restituisce il risultato atteso”. Asserisci la regola specifica (non negativo) e l'effetto collaterale specifico (sconto memorizzato una sola volta).
Per ogni invariante, aggiungi una breve nota nel test su quali dati la violerebbero. Questo impedisce che il test si trasformi nel tempo in un controllo happy-path.
Un semplice schema che regge nel tempo:
I test ad alto segnale spesso sono quelli che confermano che il codice fallisce in modo sicuro. Se un modello genera solo test happy-path, impari quasi nulla su come la feature si comporta quando input e dipendenze diventano difficili.
Inizia decidendo cosa significa “sicuro” per quella feature. Restituisce un errore tipato? Fa fallback a un default? Ritenta una volta e poi si ferma? Scrivi il comportamento atteso in una frase, poi fai sì che i test lo dimostrino.
Quando chiedi a Claude Code test per le failure mode, mantieni l'obiettivo stretto: copri i modi in cui il sistema può rompersi e asserisci la risposta esatta che vuoi. Una linea utile è: “Preferisci meno test con asserzioni più forti piuttosto che molti test superficiali.”
Categorie di failure che danno spesso i migliori test:
Esempio: hai un endpoint che crea un utente e chiama un servizio email per inviare un messaggio di benvenuto. Un test a basso valore verifica “restituisce 201.” Un test di failure ad alto segnale verifica che se il servizio email va in timeout, tu o (a) crei comunque l'utente e ritorni 201 con un flag “email_pending”, oppure (b) ritorni un 503 chiaro e non crei l'utente. Scegli un comportamento, poi asserisci sia la risposta sia gli effetti collaterali.
Testa anche cosa non fai trapelare. Se la validazione fallisce, assicurati che nulla venga scritto nel database. Se una dipendenza ritorna un payload corrotto, assicurati di non lanciare eccezioni non gestite o restituire stack trace raw.
I set di test a basso valore nascono spesso quando il modello è premiato per il volume. Se il tuo prompt chiede “20 unit test”, spesso ottieni piccole variazioni che sembrano esaustive ma non intercettano novità.
Trappole comuni:
Esempio: immagina una funzione “create user”. Dieci test happy-path potrebbero variare la stringa email e comunque perdere il punto importante: respingere email duplicate, gestire password vuote e garantire che gli ID utente restituiti siano unici e stabili.
Guardrail utili in revisione:
Immagina una feature: applicare un codice coupon al checkout.
Contratto (piccolo e testabile): dato un subtotale del carrello in centesimi e un coupon opzionale, restituisci un totale finale in centesimi. Regole: i coupon percentuali arrotondano per difetto al centesimo più vicino, i coupon fissi sottraggono una somma fissa e i totali non vanno mai sotto 0. Un coupon può essere invalido, scaduto o già usato.
Non chiedere “test per applyCoupon()”. Chiedi test per casi limite, modalità di errore e invarianti legate a questo contratto.
Scegli input che tendono a rompere la matematica o la validazione: stringa coupon vuota, subtotal = 0, subtotal appena sotto e sopra una spesa minima, uno sconto fisso maggiore del subtotale e una percentuale come 33% che crea problemi di arrotondamento.
Assumi che la lookup del coupon possa fallire e che lo stato possa essere errato: il servizio coupon è giù, il coupon è scaduto o il coupon è già stato riscattato dall'utente. Il test dovrebbe dimostrare cosa succede dopo (coupon rifiutato con errore chiaro, totale invariato).
Un set minimo e ad alto segnale di test (5 test) e cosa cattura ciascuno:
Se questi passano, hai coperto i punti di rottura comuni senza riempire la suite con test happy-path duplicati.
Prima di accettare ciò che il modello genera, fai un rapido controllo qualità. L'obiettivo è test che proteggano ognuno da un bug specifico e probabile.
Usa questa checklist come gate:
Un trucco pratico dopo la generazione: rinomina i test in “should <comportamento> when <condizione limite>” e “should not <esito negativo> when <failure>”. Se non riesci a rinominarli chiaramente, non sono focalizzati.
Se costruisci con Koder.ai, questa checklist si integra bene con snapshot e rollback: genera test, eseguili e fai rollback se il nuovo set aggiunge rumore senza migliorare la copertura.
Tratta il tuo prompt come un'arnese riutilizzabile, non come una richiesta una tantum. Salva un blueprint del prompt (quello che forza confini, modalità di errore e invarianti) e riusalo per ogni nuova funzione, endpoint o flow UI.
Un'abitudine semplice che migliora i risultati: chiedi una frase per test che spieghi quale bug catturerebbe. Se quella frase è generica, il test è probabilmente rumore.
Tieni una lista viva di invarianti di dominio per il tuo prodotto. Non tenerla solo in testa. Aggiungila ogni volta che trovi un bug reale.
Un workflow leggero che puoi ripetere:
Se costruisci app via chat, esegui questo ciclo dentro Koder.ai (koder.ai) così contratto, piano e test generati vivono nello stesso posto. Quando un refactor cambia comportamento inaspettatamente, snapshot e rollback rendono più semplice confrontare e iterare finché il set ad alto segnale resta stabile.
Default: punta a un insieme ridotto che sarebbe in grado di intercettare un bug reale.
Un limite pratico che funziona bene è 6–10 test per unità (funzione/modulo). Se servono più test, di solito vuol dire che la unit fa troppo o il contratto non è chiaro.
I test happy-path provano principalmente che il tuo esempio continua a funzionare. Spesso non intercettano i problemi che emergono in produzione.
I test ad alto segnale mirano a:
Inizia con un piccolo contratto che si possa leggere in un soffio:
Poi genera i test da quel contratto, non solo dagli esempi.
Testa prima questi casi:
Un buon test per una modalità di errore dimostra due cose:
Se c'è una scrittura su DB, verifica sempre cosa è successo nello storage dopo l'errore.
Approccio predefinito: trasforma l'invariante in un'asserzione sugli esiti osservabili.
Esempi:
expect(total).toBeGreaterThanOrEqual(0)Vale la pena mantenere un test happy-path quando protegge un'invariante o un'integrazione critica.
Buone ragioni per tenerne uno:
Altrimenti, scambialo con test di confine o di failure che intercettano più classi di bug.
Spingi per PHASE 1: solo piano prima di generare codice.
Richiedi al modello di fornire:
Solo dopo aver approvato il piano, chiedi di generare il codice. Questo evita output tipo “20 test tutti uguali”.
Predefinito: mocha/ jest / altro — mocka solo il confine che non controlli (DB/network/clock) e lascia il resto reale.
Per evitare over-mocking:
Se un test si rompe dopo un refactor ma il comportamento non è cambiato, spesso è troppo mockato o troppo accoppiato all'implementazione.
Usa una semplice prova di cancellazione:
Controlla anche duplicati:
Scegline uno o due per dimensione di input in modo che ogni test copra un rischio unico.
Preferisci controllare sia il valore di ritorno che gli effetti collaterali, perché molti bug si nascondono in “ha restituito OK ma ha scritto la cosa sbagliata”.