Å feile er menneskelig - Automatisert testing av Delphi kode med dunit. Av Kris GolkoTo introdusere bugs er menneske; problemet er vanligvis ikke med å fikse dem, men med å finne dem før kundene gjør. Automatiserte testverktøy komme til unnsetning her, med sin evne til å gjenta testprosessen nådeløst, slik at utviklere kan jobbe med mye mer selvtillit. Hvorfor velge dunit? Fordi det handler om enhetstesting (enhet i betydningen av å bygge en blokk med kode i stedet for en Object Pascal enhet). En annen fordel er at dunit testene er rammeverk innenfor for å kjøre kode, noe som er raskere og mer praktisk enn å kjøre en hel applikasjon. Det jeg liker mest i dunit er at jeg kan lage min test tilfeller med min favoritt utviklingsverktøy. Dunit støtter Delphi 4 til 7 samt Kylix.Getting startedDUnit er distribuert som kildekode; Det består av en rekke Delphi kildefiler, eksempler og dokumentasjon. Den kan lastes ned fra prosjektets nettside på SourceForge sourceforge.net:/projects/dunit. For å installere, bare pakk ut filen til en katalog av ditt valg, bevare underkataloger. Enhetene brukes til å opprette og kjøre tester er i «src» katalogen som bør enten legges til Library Path i "Miljø Alternativer | Library" eller Search Path of Project | Valg i din test project.How skrive test casesThis er lett : alt du trenger å gjøre er å lage en klasse arvet fra TTestCase er TTestCase deklarert i TestFramework enhet. For å legge til tester til en test klasse, er å legge en publisert prosedyre for hver test til din avledet klasse. En test runner objektet bruker RTTI til å oppdage hva tester er tilgjengelige i en test klasse.Det er to svært nyttige metoder: oppsett og demontering. Gjennomføring av hver enkelt test i en klasse begynner med oppsett, så kommer den publiserte prosedyre som utgjør testen, som deretter fulgt av Teardown. Oppsett kan brukes på å bruke gjenstander som trengs for å kjøre tester og initiere sine stater i henhold til test virksomhet. Oppsett bør også inneholde tiltak som trengs for å bli utført før hver test er utført, som å koble til en database. Teardown metode bør brukes til å rydde opp og kast objects.A publisert prosedyren vanligvis inneholder flere samtaler til Check-metoden. Check-metoden er deklarert som følger:
prosedyre Check (Forhold: boolean, msg: string = ''); virtuell;
Tilstanden parameteren er vanligvis i form av et uttrykk som evaluerer til en boolsk verdi. . Hvis uttrykket gått inn i Check prosedyren evalueres til false så testen er merket som mislyktes og gjennomføringen av testen er avbrutt
... Check (Forventet = Actual, 'Faktisk antall annerledes enn forventet'); .. .
tester er organisert i bunter, kalt suiter. Test suiter kan inneholde flere tester og andre testsuiter, og dermed gi en måte å bygge et tre tester. Sentralt i dunit virksomhet er testen registret, som holder alle suitene i testprogrammet. Typisk blir test registeret inne mens en test program initialiserer. Enheter som erklærer testtilfeller av konvensjonen har en initialisering delen der testtilfelle blir laget og lagt til registry.Test suitene kan skapes ved å opprette forekomster av TTestSuite klassen erklært i TestFramework. Den mest praktiske og ofte brukt måte å lage en testsuite er å bruke metoden Suite for TTestCase klasse, noe som skaper en ny TTestSuite som bare inneholder Testcase: oppmerksom på at det er en klasse method.The RegisterTest og RegisterTests prosedyrer legge tester eller testsuiter til test registret. Det enkleste eksempelet er å lage en testsuite som inneholder en enkelt test, og deretter registrere det som følger:
Framework.RegisterTest (TMyTest.Suite);
Slik kjører testsDUnit inkluderer to standard test runner klasser: TGUITestRunner med interaktive grafiske grensesnitt og TTextTestRunner med batch-modus kommandolinje-grensesnitt. TGUITestRunner er deklarert i GUITestRunner enhet sammen med RunRegisteredTests stående prosedyre, som går de registrerte testsuiter bruker TGUITestRunner. CLX-versjonen av TGUITextRunner er deklarert i QGUITestRunner unit.TTextTestRunner er erklært TextTestRunner enhet sammen med tilsvarende RunRegisteredTests stående prosedyre. Samtaler til RunRegisteredTest er vanligvis kvalifisert med en enhet navn, siden det er flere globale RunRegisteredTests prosedyrer, for eksempel:
GUITestRunner.RunRegisteredTest;
Eksisterende brukere av dunit vil legge merke til at en av de nyere endringer dunit er tillegg av RunRegisteredTests klassen metoder og nedgraderingen av frittstående RunRegisteredTests, siden flere globale prosedyrer med samme navn unødvendig rot navnerommet. Kallet til RunRegisteredTests anbefales nå å bli kvalifisert med testen løperen klassenavnet i stedet for en enhet navn:
TGUITestRunner.RunRegisteredTest;
En exampleLet oss sette det hele i praksis. Eksempelet programmet samler karakterer på den måten at mange nettsteder gir en måte å "vurdere dette nettstedet". Mest spesielt har CodeCentral en interessant rangeringssystem der du kan angi valg i henhold til dine personlige preferanser (min favoritt er gammel gresk mytologi). Den grunnleggende logikken i vårt eget rangeringssystem er definert av IRateCollector grensesnittet
IRatingCollector = grensesnitt funksjon GetPrompt: string;. Prosedyren Rate (Rater: string; Choice: heltall); funksjon GetChoiceCount: integer; funksjon GetChoice (Choice: heltall): string; funksjons GetChoiceRatings (valg: integer): heltall; funksjon GetRatingCount: integer; funksjon GetRating (Indeks: integer; Var Rater: string): integer; funksjon GetRatersRating (Rater: string): integer; end;
Den TRateTests klassen tillater oss å utvikle og teste forretningslogikk uavhengig fra å utvikle brukergrensesnittet. Det er faktisk en fordel å utvikle og teste forretningslogikk før du starter brukergrensesnittet. De testtilfelle blir konstruert for å teste eventuell gjennomføring av IRatingCollector
TRateTests = klasse (TTestCase) private FRateCollector. IRatingCollector; beskyttet prosedyre oppsett; styre; prosedyre Teardown; styre, publiserte //tester hereend;
Den oppsett og demontering prosedyrer brukes på å bruke og avhende en implementering av IRatingCollector
const SAMPLE_RATE_PROMPT = 'Ranger dunit (mytologiske slavisk Feminin).'; SAMPLE_RATE_CHOICES: array [0..3] snor = ( 'Lada', 'Jurata', 'Marzanna', 'Baba Jaga'); prosedyre TRateTests.Setup, begynner //endre denne linjen bare for å teste en annen implementering av IRatingCollector FRateCollector : = TSimpleRatingCollector.Create (SAMPLE_RATE_PROMPT, SAMPLE_RATE_CHOICES); end; prosedyre TRateTests.TearDown, begynner FRateCollector: = null; end;
Prosedyrer erklært i den publiserte delen er grunnleggende testing enheter; skrive dem krever oppfinnsomhet og kreativitet. I vårt eksempel TestChoices sjekker om listen over valg er som forventet
prosedyre TRateTests.TestChoices, Var jeg. Heltall; begynne Check (FRatingCollector.GetChoiceCount = Lengde (SAMPLE_RATE_CHOICES), Format ( 'Det bør være nøyaktig% d valg ', Lengde (SAMPLE_RATE_CHOICES)])); for jeg: = 0 til FRatingCollector.GetChoiceCount - en gjør Check (FRatingCollector.GetChoice (I) = SAMPLE_RATE_CHOICES [I], 'Forventet' + SAMPLE_RATE_CHOICES [I]); end; Book De TestRate prosedyre sjekker om utføring hastigheten prosedyren resulterer i økende antall priser på merke valg:
Prosedyre TestRate ... FRatingCollector.Rate (NextRater, 0); Sjekk (FRatingCollector.GetRatingCount = rating + 1, "Forventet '+ IntToStr (rating + 1)); ... end;
Tester bør være så omfattende som mulig, men det er veldig vanskelig å dekke alle mulige scenarier . Mens bugs blir rapportert, bør tester revised.It er svært viktig at tester dekke for ekstreme forhold; i vårt eksempel, kan valget eller rater sendes til Rate prosedyren være ugyldig. Testene sjekke om et unntak heves når et unntak er forventet. Følgende kode sjekker om EinvalidRater unntak heves når en rater prøver å rangere andre gang
ErrorAsExpected: = false; Rater:. = NextRater, prøv FRatingCollector.Rate (Rater, 0); FRatingCollector.Rate (Rater, 0), unntatt //unntak ventet på E: EInvalidRater gjøre ErrorAsExpected: = true; ende; Sjekk (ErrorAsExpected, 'Unntak forventes hvis en rater har allerede vurdert'); Book slutt, registrering av testen i initialisering avsnitt:
RegisterTest ( 'Grunnleggende tester », [TRateTests.Suite]);
prosjektet filen~~POS=HEADCOMP viser den typiske måten å kjøre tester i GUI mode
. GUITestRunner.runRegisteredTests;
å bruke CLX snarere enn VCL, erstatte GUITestRunner med QGUITestRunner i kvalifiserte samtaler til runRegisteredTests samt i bruken clause.Extreme plattform programmingWhen porting dunit til Kylix, er det to kategorier av problemer, forskjellen mellom systemkall Windows og Linux og forskjellene mellom VCL og CLX. Overraskende, dekker OS forskjellene er enklere. Det første trinnet er å sette betinget utsagn i bruker klausuler, enheter som Windows eller meldinger er selvfølgelig ikke tilgjengelig i Kylix; prototyper av grunnleggende systemkall kan bli funnet i libc enhet og grunnleggende typedefinisjoner i Typer og Qt standard enheter. Noen systemfunksjoner har blitt erstattet av Linux-ekvivalenter, andre må være implemented.It tok en rekke triks for å port til CLX. CLX og VCL visuelle komponenter er bare litt annerledes, men til tross for dette, noen ganger porting kan være ganske difficult.DUnit for Delphi og Kylix kompilerer fra samme kilde, med unntak av GUITestRunner /QGUITestRunner GUI testløpere, er det imidlertid en rekke betinget utsagn i kilden for å aktivere denne kryssplattform support.This er bare beginningOnce det grunnleggende er mestret, er det mange flere funksjoner i dunit som muliggjør mer komplekse tester som skal utføres. For eksempel er det ferdige klasser som kan anvendes for et bestemt formål, for eksempel for å teste en minnelekkasje. Den TestExtension Enheten inneholder en rekke nyttige klasser basert på Decorator design mønster. En av de viktigste er TRepeatedTest klasse, som gjør det mulig å utføre en test et gitt antall ganger. I dette eksemplet er TRepeatedTest brukes til å ringe Rate prosedyren flere ganger etter hverandre
RegisterTest ( 'Gjentatt Rate ", TRepeatedTest.Create (TRateTests.Create (' CheckRate '), 5));.
klassen TGUITestCase, støtter testing av grafiske grensesnitt. TGUITestCase er deklarert i GUITesting enhet. Se RateGUITests enhet for et eksempel på bruk den til å teste dialogboksen for å sende inn rating.As Delphi kilde for dunit er fritt tilgjengelig, slik at en erfaren Delphi utviklere kan enkelt utvide dunit, for eksempel ved å skape nye extensions.Testing som en frigjort artTesting bringer bedre resultater hvis testene er basert på kunnskap om søknaden design. Hvis UML diagrammer har blitt opprettet, kan de brukes som grunnlag for bygging av testene, og dermed fullføre kravet, analyse, implementering, testing cycle.Ideally, bør testene utvikles samtidig som utvikling av prosjektkode. Teknisk sett kan prøver lages for anvendelser som allerede er fullført, men disse programmer er ofte ikke er egnet for enhetstesting, siden de ikke har en velutviklet modulær struktur. Ved hjelp av automatisert testing med dunit fremmer bedre program design samt gjøre det enklere å refactor kode, men jeg tror den største forskjellen det gjør er på det stadiet av søknaden vedlikehold. Prioriteringa kan tildeles enheter (i betydningen av moduler) i stedet for komplette prosjekter, og de kan bug fix og testenheter uten å bygge og teste en hel applikasjon. Noen ganger et problem kan løses på enhetsnivå og involvering av en erfaren utvikler er nødvendig, men i tilfelle av en stor amorphic søknad, opplevde utviklere har til å være involvert hele tiden.