Leer een Claude Code testgeneratie-prompt die hoge-signaal tests produceert door grenzen, invarianties en faalmodi te targeten in plaats van happy paths.

Auto-gegenereerde testsuites zien er vaak indrukwekkend uit: tientallen tests, veel setup-code, en elke functienaam komt ergens terug. Maar veel van die tests zijn slechts “het werkt als alles normaal is”-controles. Ze slagen makkelijk, vangen zelden bugs en kosten nog steeds tijd om te lezen en te onderhouden.
Met een typische Claude Code testgeneratie-prompt heeft het model de neiging de voorbeeld-inputs te spiegelen die het ziet. Je krijgt variaties die er anders uitzien maar hetzelfde gedrag dekken. Het resultaat is een grote suite met dunne dekking waar het belangrijk is.
High-signal tests zijn anders. Het zijn de kleine set die de storing van vorige maand had opgevangen. Ze falen wanneer het gedrag op een risicovolle manier verandert, en ze blijven stabiel bij onschuldige refactors. Eén high-signal test kan twintig "returned the expected value"-checks waard zijn.
Low-value happy-path generatie heeft meestal een paar duidelijke signalen:
Stel je een functie voor die een kortingscode toepast. Happy-path tests bevestigen dat “SAVE10” de prijs verlaagt. Echte bugs verstoppen zich elders: 0 of negatieve prijzen, verlopen codes, afrondingsranden of maximale kortingcaps. Dat zijn de gevallen die leiden tot verkeerde totalen, boze klanten en nachtelijke rollbacks.
Het doel is te verschuiven van “meer tests” naar “betere tests” door te mikken op drie doelen: grenzen, faalmodi en invarianties.
Als je high-signal unit tests wilt, stop dan met vragen om “meer tests” en begin te vragen om drie specifieke soorten. Dit is de kern van een Claude Code testgeneratie-prompt die nuttige dekking oplevert in plaats van een stapel “werkt op normale input”-controles.
Grenzen zijn de randen van wat de code accepteert of produceert. Veel echte defecten zijn off-by-one, lege-staat of timeout-problemen die nooit in een happy path verschijnen.
Denk in termen van minima en maxima (0, 1, max lengte), leeg versus aanwezig ("", [], nil), off-by-one (n-1, n, n+1) en tijdslimieten (dicht bij de cutoff).
Voorbeeld: als een API “tot 100 items” accepteert, test dan 100 en 101, niet alleen 3.
Faalmodi zijn de manieren waarop het systeem kan breken: slechte inputs, ontbrekende afhankelijkheden, gedeeltelijke resultaten of upstream errors. Goede faalmodus-tests controleren gedrag onder stress, niet alleen output onder ideale omstandigheden.
Voorbeeld: als een database-call faalt, retourneert de functie dan een duidelijke fout en vermijdt het het schrijven van gedeeltelijke data?
Invarianties zijn waarheden die voor en na een oproep waar moeten blijven. Ze veranderen vage correctheid in scherpe asserties.
Voorbeelden:
Wanneer je je richt op deze drie doelen, krijg je minder tests, maar draagt elke test meer signaal.
Als je te vroeg om tests vraagt, krijg je meestal een hoop beleefde “werkt zoals verwacht”-checks. Een eenvoudige oplossing is eerst een klein contract te schrijven en vervolgens tests van dat contract te genereren. Het is de snelste manier om een Claude Code testgeneratie-prompt om te zetten in iets dat echte bugs vindt.
Een bruikbaar contract is kort genoeg om in één adem te lezen. Mik op 5 tot 10 regels die drie vragen beantwoorden: wat gaat erin, wat komt eruit en wat verandert er nog meer.
Schrijf het contract in gewone taal, niet in code, en neem alleen op wat je kunt testen.
Als je dat hebt, scan het op plekken waar de realiteit je aannames kan breken. Die worden grensgevallen (min/max, nul, overflow, lege strings, duplicaten) en faalmodi (timeouts, permission denied, unique constraint violations, corrupte input).
Hier is een concreet voorbeeld voor een feature zoals reserveInventory(itemId, qty):
Het contract kan zeggen dat qty een positief geheel getal moet zijn, de functie atomair moet zijn en nooit een negatieve voorraad mag creëren. Dat suggereert direct high-signal tests: qty = 0, qty = 1, qty groter dan beschikbaar, gelijktijdige calls en een geforceerde databasefout halverwege.
Als je een vibe-coding tool gebruikt zoals Koder.ai, geldt dezelfde workflow: schrijf eerst het contract in chat, genereer daarna tests die direct grenzen, faalmodi en de “mag nooit gebeuren”-lijst aanvallen.
Gebruik deze Claude Code testgeneratie-prompt wanneer je minder tests wilt, maar elke test meer waarde moet hebben. De sleutel is eerst een testplan forceren en pas na goedkeuring testcode genereren.
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.
Een praktische truc is te eisen dat de boundary-matrix als compacte tabel komt, zodat gaten duidelijk zijn:
| Dimension | Valid edge | Just outside | “Weird” value | Expected behavior |
|---|---|---|---|---|
| length | 0 | -1 | 10,000 | error vs clamp vs accept |
Als Claude 20 tests voorstelt, duw dan terug. Vraag het te combineren en alleen die tests te houden die echt een bug zouden vangen (off-by-one, verkeerd fouttype, stil dataverlies, gebroken invariant).
Begin met een klein, concreet contract voor het gedrag dat je wilt. Plak de functiehandtekening, een korte beschrijving van inputs en outputs en eventuele bestaande tests (zelfs als het alleen happy-paths zijn). Dit houdt het model verankerd in wat de code daadwerkelijk doet, niet in wat het raadt.
Vraag vervolgens om een risico-tabel voordat je om enige testcode vraagt. Eis drie kolommen: grensgevallen (randen van geldige input), faalmodi (slechte input, ontbrekende data, timeouts) en invarianties (regels die altijd waar moeten zijn). Voeg één zin per rij toe: “waarom dit kan breken.” Een eenvoudige tabel onthult sneller gaten dan een stapel testbestanden.
Kies daarna de kleinste set tests waarbij elke test een uniek bug-vangend doel heeft. Als twee tests om dezelfde reden falen, houd de sterkere.
Een praktische selectieregel:
Eis tenslotte een korte uitleg per test: welke bug zou hij vangen als hij faalt. Als de uitleg vaag is ("valideert gedrag"), is de test waarschijnlijk low-signal.
Een invariant is een regel die altijd waar moet blijven ongeacht welke geldige input je doorgeeft. Met invariant-based testing schrijf je eerst de regel in gewone taal en zet je die om in een assertie die luid kan falen.
Kies 1 of 2 invarianties die je echt beschermen tegen echte bugs. Goede invarianties gaan vaak over veiligheid (geen dataverlies), consistentie (zelfde input,zelfde output) of limieten (nooit caps overschrijden).
Schrijf de invariant als een korte zin, bepaal dan welk bewijs je test kan observeren: return values, opgeslagen data, uitgezonden events of calls naar dependencies. Sterke asserties controleren zowel uitkomst als bijwerkingen, want veel bugs verstoppen zich in “het returned OK, maar schreef het verkeerde”.
Bijvoorbeeld, stel je hebt een functie die een coupon op een order toepast:
Encodeer die nu als concrete asserties:
expect(result.total).toBeGreaterThanOrEqual(0)
expect(db.getOrder(orderId).discountCents).toBe(originalDiscountCents)
Vermijd vage asserts zoals “returns expected result”. Assert de specifieke regel (niet-negatief) en de specifieke bijwerking (korting eenmaal opgeslagen).
Voor elke invariant voeg je een korte notitie in de test toe over welke data het zou schenden. Dit voorkomt dat de test later verandert in een happy-path check.
Een simpel patroon dat standhoudt in de tijd:
High-signal tests zijn vaak degene die bevestigen dat je code veilig faalt. Als een model alleen happy-path tests schrijft, leer je vrijwel niets over hoe de feature zich gedraagt wanneer inputs en dependencies rommelig worden.
Begin met bepalen wat “veilig” betekent voor deze feature. Returned het een getypeerde fout? Valt het terug op een default? Probeert het één keer opnieuw en stopt dan? Schrijf dat gewenste gedrag in één zin en laat de tests het bewijzen.
Als je Claude Code om failure-mode tests vraagt, houd het doel strikt: dek de manieren waarop het systeem kan breken en assert exact de respons die je wilt. Een nuttige lijn is: “Geef de voorkeur aan minder tests met sterkere asserties boven vele platte tests.”
Faalcategorieën die vaak de beste tests opleveren:
Voorbeeld: je hebt een endpoint dat een gebruiker aanmaakt en een emailservice aanroept om een welkomstmail te sturen. Een low-value test checkt “returns 201.” Een high-signal failure test checkt dat als de emailservice time-out, je óf (a) de gebruiker toch aanmaakt en 201 returned met een "email_pending" flag, óf (b) een duidelijke 503 returned en geen gebruiker aanmaakt. Kies één gedrag en assert zowel de response als de bijwerkingen.
Test ook wat je niet lekt. Als validatie faalt, zorg dat er niets naar de database geschreven wordt. Als een dependency een corrupte payload terugstuurt, zorg dat je geen ongehandelde exception gooit of raw stacktraces returned.
Low-value testsets ontstaan vaak wanneer het model beloond wordt voor volume. Als je Claude Code testgeneratie-prompt vraagt om “20 unit tests”, krijg je vaak kleine variaties die er uitgebreid uitzien maar niets nieuws vangen.
Veelvoorkomende valkuilen:
Voorbeeld: stel een “create user” functie. Tien happy-path tests variëren de emailstring en missen nog steeds het belangrijke: duplicate emails weigeren, lege wachtwoorden afhandelen en garanderen dat geretourneerde user IDs uniek en stabiel zijn.
Beperkingen die helpen bij review:
Stel één feature: een kortingscode toepassen bij afrekenen.
Contract (klein en testbaar): gegeven een cart subtotal in cents en een optionele coupon, retourneer een final total in cents. Regels: percentagecoupons worden naar beneden afgerond naar de dichtstbijzijnde cent, vaste coupons trekken een vast bedrag af en totalen mogen nooit onder 0 komen. Een coupon kan ongeldig, verlopen of reeds gebruikt zijn.
Vraag niet “tests voor applyCoupon()”. Vraag om grensgevaltests, faalmodi en invarianties gekoppeld aan dit contract.
Kies inputs die wiskunde of validatie breken: een lege couponstring, subtotal = 0, subtotal net boven/onder een minimum spend, een vaste korting groter dan het subtotal en een percentage zoals 33% dat afronding veroorzaakt.
Ga ervan uit dat coupon-lookup kan falen en dat staat fout kan zijn: de couponservice is down, de coupon is verlopen of de coupon is al ingewisseld door deze gebruiker. De test moet bewijzen wat er vervolgens gebeurt (coupon verworpen met een duidelijke fout, total onveranderd).
Een minimale, high-signal testset (5 tests) en wat elk test:
Als deze slagen, heb je de gebruikelijke breekpunten gedekt zonder de suite te vullen met duplicerende happy-path tests.
Voer een korte kwaliteitscheck uit voordat je accepteert wat het model genereert. Het doel is tests die elk een specifiek, waarschijnlijk risico beschermen.
Gebruik deze checklist als poort:
Een snelle praktische truc na generatie: hernoem tests naar “should <gedrag> when <randvoorwaarde>” en “should not <slecht resultaat> when <falen>”. Als je ze niet netjes kunt hernoemen, zijn ze niet gefocust.
Als je bouwt met Koder.ai, past deze checklist ook goed bij snapshots en rollback: genereer tests, draai ze en rollback als de nieuwe set ruis toevoegt zonder de coverage te verbeteren.
Behandel je prompt als een herbruikbaar harnas, niet als éénmalig verzoek. Bewaar één blueprint prompt (die de grenzen, faalmodi en invarianties forceert) en hergebruik die voor elke nieuwe functie, endpoint of UI-flow.
Een eenvoudige gewoonte die resultaten snel verbetert: eis één zin per test die uitlegt welke bug hij zou vangen. Als die zin generiek is, is de test waarschijnlijk ruis.
Houd een levende lijst met domein-invarianties voor je product. Bewaar die niet alleen in je hoofd. Voeg eraan toe telkens wanneer je een echte bug vindt.
Een lichtgewicht workflow die je kunt herhalen:
Als je apps via chat bouwt, run deze cyclus binnen Koder.ai (Koder.ai) zodat contract, plan en gegenereerde tests op één plek blijven. Wanneer een refactor gedrag onverwacht verandert, maken snapshots en rollback het makkelijker om te vergelijken en te itereren totdat je high-signal set stabiel blijft.
Standaard: mik op een klein aantal tests dat een echt bug zou hebben opgevangen.
Een handige bovengrens is 6–10 tests per unit (functie/module). Als je meer nodig hebt, doet je unit waarschijnlijk te veel of is je contract niet duidelijk.
Happy-path tests bewijzen meestal alleen dat je voorbeeld nog werkt. Ze missen vaak wat er in productie misgaat.
High-signal tests richten zich op:
Begin met een klein contract dat je in één adem kunt lezen:
Genereer vervolgens tests vanuit dat contract, niet alleen vanuit voorbeelden.
Test eerst deze:
Een goede failure-mode test bewijst twee dingen:
Als er een database-write bij betrokken is, controleer altijd wat er in de opslag gebeurde na de fout.
Standaardbenadering: zet de invariant om in een assertie op observeerbare uitkomsten.
Voorbeelden:
expect(total).toBeGreaterThanOrEqual(0)Een happy-path test is het waard wanneer hij een invariant of een kritieke integratie beschermt.
Goede redenen om er één te houden:
Anders: ruil hem in voor grens-/faaltests die meer bugklassen vangen.
Duw eerst op PHASE 1: alleen plan.
Eis dat het model oplevert:
Pas na goedkeuring van het plan genereer je code. Dit voorkomt “20 look-alike tests”.
Standaard: mock alleen de boundary die je niet beheert (DB/netwerk/klok), en laat de rest echt.
Om over-mocking te vermijden:
Als een test bij een refactor breekt maar gedrag niet veranderde, is hij vaak te veel implementation-coupled of over-gemockt.
Gebruik een simpele deletetest:
Check ook op duplicaten:
Kies één of twee per input-dimensie zodat elke test een uniek risico dekt.
Controleer bij voorkeur zowel retourwaarde als bijwerkingen, omdat veel bugs zich verbergen in “return OK maar verkeerde schrijf”.