Enhet 6 / 11

Generering av enhetstest och testbarhet: Robust testning med AI

Vinster:

  • Förmåga att förhindra artificiell intelligens från att acceptera felaktigt beteende som "korrekt" genom att beräkna det förväntade värdet i enhetstester oberoende av acceptansregeln
  • Möjlighet att skriva ut snabba, oberoende och repeterbara tester genom att tillämpa AAA- och FIRST-principerna och håna externa beroenden
  • Förmåga att testa tester med mutation (kodbrott) och känna igen svårtestad kod som en designlukt

Det största och snabbaste lagret i testpyramiden är enhetstestning – testning som verifierar en funktion eller en liten kodbit isolerad från allt annat. Tusentals enhetstester körs på några sekunder och fångar en bugg medan koden fortfarande finns på utvecklarens skärm. Artificiell intelligens (AI) är kanske mest skicklig på att producera enhetstester: du ger den en funktion, AI producerar dussintals tester. Men just denna bekvämlighet ger upphov till den största fällan: AI producerar lätt tester som "glöder grönt men inte verifierar någonting" eller accepterar det nuvarande (kanske felaktiga) beteendet hos koden som "korrekt". I den här enheten kommer du att lära dig hur du skriver verkligt skyddande enhetstester med AI och förhållandet mellan testbar kod och AI.

Egenskaper för ett bra enhetstest: FÖRST

Bra enhetstester följer FIRST-principerna: Snabb, Oberoende (tester ska inte vara beroende av varandra), Repeterbara (repeterbara — samma resultat i alla miljöer), Självvaliderande (klart godkänt/underkänd), I tid (i tid). Påminn dig själv om dessa principer när du låter AI producera tester; be specifikt att testet inte är beroende av omvärlden (faktisk databas, nätverk, klocka) för att vara "oberoende" och "repeterbar".

AAA-mönster och uttrycksfull påstående

Ett solid enhetstest följer AAA-strukturen: Arrangera (förbered — ställ in ingångar och beroenden), Agera (exekvera — anropa funktionen som testas), Bekräfta (validera — jämför resultatet med det förväntade värdet). Den kritiska är hävda. Det vanligaste misstaget AI gör är att härleda påståendet från utdata från koden som testas - logiken "vad koden än returnerar är sant". Detta gör testet meningslöst. Det korrekta sättet är att bestämma det förväntade värdet oberoende (beräkna det manuellt utifrån acceptanskriterierna).

Observera: Om du säger till AI:n "skriv ett test för den här funktionen", kan AI:n köra funktionen och skriva dess utdata som "förväntat". Detta test godkänns även om funktionen är falsk. Säg istället "du beräknar de förväntade resultaten enligt dessa regler, referera inte till funktionens aktuella utdata."

Hånar, stubbar och beroenden

Enhetstestning kräver isolering. Om din funktion är beroende av en databas eller API, ersätts de med skenobjekt (mock/stub — en kontrollerad, dummy ersättning för det verkliga beroendet) vid testning. Detta gör testet snabbt, oberoende och reproducerbart. AI kan producera skeninstallation; Men akta dig för överdrivet hån: om du hånar allt kommer testet bara att verifiera "vad hånet returnerar", inte den faktiska logiken. Balans: emulera omvärlden, kör den verkliga logiken som testas.

Testbarhet och AI

Det finns en intressant feedback: kod som är svår att testa är ofta dåligt utformad kod. Om AI har problem med att skriva tester till en funktion (för många beroenden, dold global status, biverkningar), är det en designlukt. Att fråga AI:en "hur skulle du refaktorera den här koden för att göra den testbar" leder till både bättre testning och bättre kod.

Parameteriserade tester och datadiversitet

Att skriva ett separat test varje gång för att verifiera samma regel med olika input är både tråkigt och svårt att underhålla. Parameteriserad testning - en struktur som upprepade gånger kör samma testlogik på en lista med ingångar och förväntade resultat - eliminerar denna upprepning: en enda testkropp matas med dussintals ingångspar. AI är mycket effektiv på att producera dessa indata-förväntade utfallstabeller när du ger den dina godkännanderegler; I synnerhet tar den systematiskt upp gränsvärden och ekvivalensklasser.

Men det finns en fälla här också: AI:n tenderar att härleda de förväntade resultaten i den genererade tabellen från koden som testas. Detta fel är ännu farligare i parametriserad testning, eftersom en enda felaktig logik ogiltigförklarar dussintals rader. Låt därför alltid kolumnen för förväntat resultat beräknas oberoende enligt acceptansregeln och validera manuellt åtminstone några rader. Be även om en beskrivningskolumn "vad representerar varje rad"; så när en rad går sönder ser du omedelbart vilket tillstånd som är brutet.

Tips: Lägg avsiktligt till en "fällrad" i den parametriserade testtabellen - det vill säga, medvetet skriva fel resultatet. Om den linjen inte blir röd när du kör testet, verifierar ditt test faktiskt inte den situationen. Detta är en snabb mock-pass-kontroll.

Svag prompt / Stark prompt

Svag: "Skriv ett enhetstest för den här funktionen."
Stark: Skriv enhetstester för [språk/ramverk] för funktionen "taxCalculate(amount, rate). Acceptansregel: resultat = belopp * rate, avrundat till 2 decimaler; negativt belopp eller kurs ger ett fel; returnerar 0 om kursen är 0. Använd AAA-struktur. Beräkna förväntade värden manuellt enligt DESSA utdata, och hänvisa inte till DESSA, negativa fall, och hänvisa inte till de mycket negativa fallen. avrunda till decimaler). Låt namnet på varje test beskriva regeln som det verifierar "Nej".

Kraftfull uppmaning; Det ger acceptansregeln, oberoende förväntat värdeförväntning, struktur och kantfall. Således blir testet regelns väktare, inte spegeln av koden.

Kvalitetstabell för enhetstest

symptom

Dåligt test (fake-trust)

bra test

hävda

Ingen eller "inte null"

Förväntat konkret värde

Förväntad värdekälla

Utgång av funktionen

Acceptansregel / manuell beräkning

missbruk

Faktisk DB/nätverk/timme

Isolerad med mock/stub

kantfodral

Bara lycklig väg

gräns, negativ, fel

När du bryter koden

förblir grönt

blir röd

Namn

test1, testmetod

beskriver regeln den bekräftar

Fyra kopierbara mallar

1) Regeldriven enhetstestning:

Din roll: senior mjukvarutestingenjör. Skriv ett enhetstest på följande funktion med [språk/ramverk]: [signatur]. Acceptansregler: [regler].- Använd AAA-struktur.- Beräkna förväntade värden manuellt enligt DESSA regler; Referera INTE till funktionens aktuella utgång. - Täck gränsen, negativ, fel och lycklig väg med separata tester. - Låt varje testnamn beskriva regeln som den verifierar. - Håna externa beroenden; Få den faktiska logiken att fungera.

2) Mutationsmotståndskontroll:

Kolla in dessa enhetstester. Lista 5 mindre justeringar jag skulle kunna göra i koden som testas (a - istället för ett +, ett >= istället för ett >, en gränsförskjutning) och säg mig för var och en VILKA av dessa tester blir röda? Om ingen returneras är testet otillräckligt. Kod + tester: [klistra in]

3) Testbarhetsgranskning:

Varför är det svårt att skriva ett enhetstest för denna funktion? Dolt beroende, global status, biverkningar, finns det många ansvarsområden? Föreslå minimal refactoring för att göra det testbart; ändra inte beteende. Kod: [klistra in]

4) Ofullständigt scenarioavslut:

Följande funktion och tillgängliga tester ges. Lista vilket beteende/edgecase som ALDRIG har testats (scope gap) och lägg till ett test för varje. Funktion+tester: [klistra in]

tre minifodral

Fall 1 — Testa att spegla koden. En utvecklare lät AI skriva ett test för avrundningsfunktionen; 10 tester var gröna. Faktum är att funktionen rundade åt fel håll, men AI:n hade tagit de förväntade värdena från funktionens utdata, så testerna ansåg att felet var "sant". När de förväntade värdena beräknades manuellt med den "regeldrivna" mallen blev 4 test röda och det verkliga felet avslöjades.

Fall 2 — Värdet av mutationskontroll. Ett team förlitade sig på 45 enhetstester. Försökte 20 mindre justeringar av koden med en "mutation robustness check"; tester fångade bara 11 av dem. De återstående 9 störningarna passerade tyst. Teamet stärkte svaga tester; Ett verkligt beräkningsfel fångades upp av dessa förbättrade tester i nästa version.

Fall 3 — Otestbarhet är en designlukt. AI:n kunde inte skriva tester för en beställningsfunktion, den behövde hela tiden den riktiga databasen. Mallen "testbarhetsgranskning" visade att funktionen inbäddade databasåtkomst. När beroendeinjektionen togs bort kunde tester skrivas och koden blev renare.

Vanliga misstag

  • Härleda det förväntade värdet från kod. AI:n accepterar funktionsutgången som "korrekt"; test som bekräftar felaktig kod.
  • Testa utan påstående eller med trivialt påstående. "Han gjorde inte ett fel, han klarade" logik; Det bekräftar ingenting.
  • Extremt hån. Att håna allt och bara testa vad hånet returnerar; verklig logik testas inte.
  • Bara den lyckliga vägen. Förbigående gräns-, negativ- och feltillstånd.
  • Testar inte genom att bryta koden. Lita på grönt utan att kontrollera mutation.
  • Ignorera otestbarhet. Att inte känna igen och fixa dålig design istället för att driva hårda tester.

Sammanfattningsvis

Enhetstester är det snabbaste och största lagret i testpyramiden; Den fångar misstaget i det billigaste ögonblicket. AI är mycket kapabel att producera enhetstester, men dess största fallgrop är att skriva tester som antar felaktigt beteende som "korrekt" genom att härleda det förväntade värdet från själva koden. Lösning: ge godkännandereglerna, låt de förväntade värdena beräknas manuellt, tillämpa AAA- och FIRST-principerna, håna omvärlden och kör den faktiska logiken och testa varje test genom mutation (bryta koden). Kod som är svår att testa är en designskylt som behöver fixas.

Applikationsuppgift

Välj en funktion som innehåller en affärsregel från ditt eget projekt. Skriv godkännanderegler och få AI att skriva tester med mallen "regeldriven enhetstestning"; Få de förväntade värdena beräknade manuellt. Tillämpa sedan "mutation robustness check": gör minst 5 små brytningar i koden och mät hur många tester som blir röda. Lägg till nytt test för oupptäckt korruption. Rapportera hur många störningar som fångades (som mutationspoäng).

checklista

  • [ ] Jag gav godkännandereglerna och lät beräkna de förväntade värdena manuellt.
  • [ ] Jag såg till att testerna inte härledde det förväntade värdet från koden.
  • [ ] Jag har etablerat oberoende tester enligt AAA- och FIRST-riktlinjerna.
  • [ ] Jag hånade de externa beroenden och körde själva logiken.
  • [ ] Jag täckte limit-, negativ- och felfall.
  • [ ] Genom att bryta koden (mutationen) bevisade jag att testerna verkligen skyddar.