Eenheid 6 / 11

Generatie en testbaarheid van unittests: robuust testen met AI

Winst:

  • Mogelijkheid om te voorkomen dat kunstmatige intelligentie foutief gedrag als 'correct' accepteert door de verwachte waarde in unit-tests onafhankelijk van de acceptatieregel te berekenen
  • Mogelijkheid om snelle, onafhankelijke en herhaalbare tests af te drukken door AAA- en FIRST-principes toe te passen en externe afhankelijkheden te bespotten
  • Mogelijkheid om tests met mutatie te testen (codebreken) en moeilijk te testen code te herkennen als een ontwerpgeur

De grootste en snelste laag van de testpiramide is unit-testen: testen waarbij een functie of een klein stukje code afzonderlijk van al het andere wordt geverifieerd. Duizenden unit-tests worden binnen enkele seconden uitgevoerd en ontdekken een bug terwijl de code nog op het scherm van de ontwikkelaar staat. Kunstmatige intelligentie (AI) is misschien wel het meest bedreven in het produceren van unit-tests: jij geeft er een functie aan, AI produceert tientallen tests. Maar juist dit gemak brengt de grootste valkuil met zich mee: AI produceert gemakkelijk tests die “groen oplichten maar niets verifiëren” of het huidige (misschien gebrekkige) gedrag van de code als “correct” accepteren. In deze unit leer je hoe je echt beschermende unit-tests met AI schrijft en de relatie tussen testbare code en AI.

Kwaliteiten van een goede unit-test: EERST

Goede unit-tests volgen de FIRST-principes: snel, onafhankelijk (tests mogen niet van elkaar afhankelijk zijn), herhaalbaar (herhaalbaar – hetzelfde resultaat in elke omgeving), zelfvaliderend (duidelijk geslaagd/mislukt), tijdig (op tijd). Herinner jezelf aan deze principes wanneer je AI tests laat produceren; vraag specifiek dat de test niet afhankelijk is van de buitenwereld (daadwerkelijke database, netwerk, klok) om "onafhankelijk" en "herhaalbaar" te zijn.

AAA-patroon en expressieve bewering

Een solide unit-test volgt de AAA-structuur: Arrange (voorbereiden – invoer en afhankelijkheden instellen), Act (uitvoeren – roep de te testen functie aan), Assert (valideren – vergelijk het resultaat met de verwachte waarde). De kritische is beweren. De meest voorkomende fout die AI maakt, is het afleiden van de bewering uit de uitvoer van de te testen code: de logica ‘wat de code ook retourneert, is waar’. Dit maakt de test zinloos. De juiste manier is om de verwachte waarde zelfstandig te bepalen (uit de acceptatiecriteria, handmatig berekenen).

Let op: als u de AI vertelt "schrijf een test voor deze functie", kan de AI de functie uitvoeren en de uitvoer ervan schrijven als "verwacht". Deze test slaagt zelfs als de functie onwaar is. Zeg in plaats daarvan: "u berekent de verwachte resultaten volgens deze regels, en verwijst niet naar de huidige uitvoer van de functie."

Mocks, stubs en afhankelijkheden

Unit-testen vereisen isolatie. Als uw functie afhankelijk is van een database of API, worden deze tijdens het testen vervangen door nepobjecten (mock/stub - een gecontroleerde, dummy-vervanger voor de echte afhankelijkheid). Hierdoor is de test snel, onafhankelijk en reproduceerbaar. AI kan een nepinstallatie produceren; Maar pas op voor overdreven spot: als je alles bespot, verifieert de test alleen "wat de mock retourneert", niet de feitelijke logica. Balans: emuleer de buitenwereld, voer de echte logica uit die wordt getest.

Testbaarheid en AI

Er is interessante feedback: code die moeilijk te testen is, is vaak slecht ontworpen code. Als de AI moeite heeft met het schrijven van tests naar een functie (te veel afhankelijkheden, verborgen globale status, bijwerkingen), is dat een ontwerpgeur. Als je aan de AI vraagt ​​‘hoe zou je deze code refactoren om hem testbaar te maken’, leidt dit tot zowel beter testen als betere code.

Geparametriseerde tests en datadiversiteit

Elke keer een aparte test schrijven om dezelfde regel met verschillende invoer te verifiëren, is zowel vervelend als moeilijk te onderhouden. Geparametriseerd testen – een structuur die herhaaldelijk dezelfde testlogica uitvoert op een lijst met inputs en verwachte resultaten – elimineert deze herhaling: een enkele testinstantie wordt gevoed met tientallen inputparen. AI is zeer efficiënt in het produceren van deze invoer-verwachte uitkomsttabellen als u er uw acceptatieregels aan geeft; In het bijzonder worden grenswaarden en gelijkwaardigheidsklassen systematisch in tabelvorm weergegeven.

Maar ook hier schuilt een valkuil: de AI heeft de neiging de verwachte resultaten in de gegenereerde tabel af te leiden uit de te testen code. Deze fout is zelfs nog gevaarlijker bij geparametriseerde tests, omdat een enkele onjuiste logica tientallen regels ongeldig maakt. Laat daarom altijd de verwachte resultaatkolom onafhankelijk berekenen volgens de acceptatieregel en valideer minimaal enkele rijen handmatig. Vraag ook om een ​​beschrijvingskolom "wat vertegenwoordigt elke rij"; dus als een rij kapot gaat, zie je meteen welke staat kapot is.

Tip: Voeg opzettelijk een “trap-rij” toe aan de geparametriseerde testtabel, dat wil zeggen dat u het resultaat willens en wetens verkeerd typt. Als die lijn niet rood wordt wanneer u de test uitvoert, verifieert uw test die situatie niet daadwerkelijk. Dit is een snelle proefpascontrole.

Zwakke prompt/sterke prompt

Zwak: "Schrijf een eenheidstest voor deze functie."
Sterk: schrijf eenheidstests in [taal/framework] voor de functie "taxCalculate(bedrag, tarief). Acceptatieregel: resultaat = bedrag * tarief, afgerond op 2 decimalen; een negatief bedrag of tarief levert een fout op; retourneert 0 als het tarief 0 is. Gebruik de AAA-structuur. Bereken de verwachte waarden handmatig volgens DEZE regels; verwijs niet naar de huidige uitvoer van de functie. Bestrijk gebonden en negatieve hoofdletters (0, negatief, zeer groot, rond af op decimalen). Laat de naam van elke test beschrijven de regel die wordt geverifieerd. Externe afhankelijkheid 'Nee'.

Krachtige prompt; Het geeft de acceptatieregel, onafhankelijke verwachte waardeverwachting, structuur en randgevallen. Zo wordt de test de bewaker van de regel, en niet de spiegel van de code.

Eenheidstestkwaliteitstabel

symptoom

Slechte test (nepvertrouwen)

goede proef

beweren

Geen of "niet null"

Verwachte concrete waarde

Verwachte waardebron

Uitvoer van de functie

Acceptatieregel / handmatige berekening

verslaving

Werkelijke DB/netwerk/uur

Geïsoleerd met mock/stub

rand geval

Alleen een gelukkige weg

limiet, negatief, fout

Wanneer je de code breekt

blijft groen

wordt rood

Naam

test1, testmethode

beschrijft de regel die het bevestigt

Vier kopieerbare sjablonen

1) Regelgestuurde unit-tests:

Jouw rol: senior software test engineer.Schrijf een unittest over de volgende functie met [taal/framework]: [handtekening].Acceptatieregels: [regels].- Gebruik AAA-structuur.- Bereken handmatig de verwachte waarden volgens DEZE regels; Verwijs NIET naar de huidige uitvoer van de functie. - Bedek het limiet-, negatieve, fout- en gelukkige pad met afzonderlijke tests. - Laat elke testnaam de regel beschrijven die deze verifieert. - Mock externe afhankelijkheden; Zorg ervoor dat de werkelijke logica werkt.

2) Mutatieweerstandscontrole:

Bekijk deze unittests eens. Noem vijf kleine aanpassingen die ik zou kunnen aanbrengen in de te testen code (a - in plaats van een +, a >= in plaats van a >, een grensverschuiving) en vertel me voor elke test WELKE van deze tests wordt rood? Als er geen wordt geretourneerd, is de test onvoldoende. Code + tests: [plakken]

3) Testbaarheidsbeoordeling:

Waarom is het moeilijk om een ​​unit-test voor deze functie te schrijven? Verborgen verslaving, mondiale status, bijwerkingen, zijn er veel verantwoordelijkheden? Stel minimale refactoring voor om het testbaar te maken; gedrag niet veranderen. Code: [plakken]

4) Onvolledige voltooiing van het scenario:

De volgende functie en beschikbare tests worden gegeven. Maak een lijst van welk gedrag/edgecase NOOIT is getest (scope gap) en voeg voor elk gedrag een test toe. Functie+testen: [plakken]

drie minikoffers

Geval 1 — Test het spiegelen van de code. Een ontwikkelaar liet de AI een test schrijven voor de afrondingsfunctie; 10 testen waren groen. In feite rondde de functie de verkeerde richting af, maar de AI had de verwachte waarden uit de uitvoer van de functie gehaald, dus de tests beschouwden de fout als "waar". Toen de verwachte waarden handmatig werden berekend met de ‘rule-driven’ template, werden 4 tests rood en kwam de echte fout aan het licht.

Geval 2 — De waarde van mutatiecontrole. Eén team vertrouwde op 45 eenheidstests. Ik heb 20 kleine aanpassingen aan de code geprobeerd met een "mutatierobuustheidscontrole"; tests hebben slechts 11 van hen gevangen. De overige 9 verstoringen verliepen geruisloos. Het team versterkte zwakke tests; Bij deze verbeterde tests in de volgende release werd een daadwerkelijke rekenfout ontdekt.

Geval 3 – Ontestbaarheid is een designgeur. De AI kon geen tests schrijven voor een bestelfunctie, hij had voortdurend de echte database nodig. Het sjabloon "testbaarheidsbeoordeling" toonde aan dat de functie databasetoegang inhield. Toen de afhankelijkheidsinjectie werd verwijderd, konden er tests worden geschreven en werd de code schoner.

Veel voorkomende fouten

  • De verwachte waarde uit code afleiden. De AI accepteert de functie-uitvoer als "correct"; test die een foutieve code bevestigt.
  • Test zonder bewering of met triviale bewering. "Hij maakte geen fout, hij slaagde"-logica; Het bevestigt niets.
  • Extreem nep. Alles bespotten en alleen testen wat de schijn teruggeeft; echte logica wordt niet getest.
  • Gewoon de gelukkige weg. Het omzeilen van limiet-, negatieve en foutstatussen.
  • Niet testen door de code te breken. Groen vertrouwen zonder te controleren op mutaties.
  • Ontestbaarheid negeren. Slecht ontwerp niet herkennen en repareren in plaats van hard testen.

Samengevat

Unit-tests vormen de snelste en grootste laag van de testpiramide; Het vangt de fout op het goedkoopste moment. AI is zeer goed in staat unit-tests te produceren, maar de grootste valkuil is het schrijven van tests die onjuist gedrag als 'correct' aannemen door de verwachte waarde uit de code zelf af te leiden. Oplossing: geef de acceptatieregels, laat de verwachte waarden handmatig berekenen, handhaaf de AAA- en FIRST-principes, bespot de buitenwereld en voer de daadwerkelijke logica uit, en test elke test op mutatie (de code breken). Code die moeilijk te testen is, is een ontwerpteken dat moet worden gerepareerd.

Applicatie taak

Selecteer een functie die een bedrijfsregel uit uw eigen project bevat. Acceptatieregels schrijven en de AI tests laten schrijven met de template ‘rule-driven unit testen’; Laat de verwachte waarden handmatig berekenen. Pas dan de ‘mutation robuustheid check’ toe: maak minimaal 5 kleine pauzes in de code en meet hoeveel testen rood kleuren. Voeg een nieuwe test toe voor onopgemerkte corrupties. Rapporteer hoeveel verstoringen zijn geconstateerd (zoals mutatiescore).

controlelijst

  • [ ] Ik gaf de acceptatieregels aan en liet de verwachte waarden handmatig berekenen.
  • [ ] Ik zorgde ervoor dat de tests niet de verwachte waarde uit de code haalden.
  • [ ] Ik heb onafhankelijke tests opgezet volgens de AAA- en FIRST-richtlijnen.
  • [ ] Ik bespotte de externe afhankelijkheden en voerde de feitelijke logica uit.
  • [ ] Ik heb limiet-, negatieve en foutgevallen besproken.
  • [ ] Door de code te verbreken (mutatie) heb ik bewezen dat de tests inderdaad beschermen.