Programmering

Test-første udvikling med FitNesse

I løbet af de sidste par år har jeg arbejdet i alle roller i testprocessen ved hjælp af serverside JavaScript, Perl, PHP, Struts, Swing og model-driven arkitekturer. Alle projekter var forskellige, men de havde alle nogle fælles: tidsfristerne løb for sent, og projekterne havde svært ved at udføre det, kunden havde virkelig havde brug for.

Hvert projekt havde en eller anden form for krav, nogle var meget detaljerede, nogle, kun få sider lange. Disse krav gennemgik normalt tre faser:

  • De blev skrevet (enten af ​​kunden eller af entreprenøren) og modtog en form for officiel accept
  • Testerne forsøgte at arbejde med kravene og fandt dem mere eller mindre utilstrækkelige
  • Projektet gik ind i en fase af accepttest, og kunden huskede pludselig alle mulige ting, som softwaren havde brug for at gøre yderligere / anderledes

Den sidste fase førte til ændringer, som førte til savnede deadlines, som satte stress på udviklerne, hvilket igen førte til flere fejl. Fejlantalet begyndte at stige hurtigt, og systemets samlede kvalitet faldt. Lyder det velkendt?

Lad os overveje, hvad der gik galt i projekterne beskrevet ovenfor: Kunden, udvikleren og testeren arbejdede ikke sammen; de videreførte kravene, men hver rolle havde forskellige behov. Derudover udviklede udviklerne normalt en slags automatiserede tests, og testerne forsøgte også at automatisere nogle tests. Normalt kunne de ikke koordinere testen tilstrækkeligt, og mange ting blev testet to gange, mens andre (normalt de hårde dele) slet ikke. Og kunden så ingen tests. Denne artikel beskriver en måde at løse disse problemer på ved at kombinere krav med automatiserede tests.

Indtast FitNesse

FitNesse er en wiki med nogle ekstra funktioner til at udløse JUnit-tests. Hvis disse tests kombineres med krav, tjener de som konkrete eksempler, hvilket gør kravene endnu mere klare. Desuden er testdataene organiseret. Det vigtigste ved brug af FitNesse er dog ide bagved, hvilket betyder, at kravene viser sig at være skrevet (delvist) som tests, hvilket gør dem testbare og derfor kan deres opfyldelse verificeres.

Ved hjælp af FitNesse kunne udviklingsprocessen se sådan ud: Kravsteknikeren skriver kravene i FitNesse (i stedet for Word). Han forsøger at få kunden involveret så meget som muligt, men det kan normalt ikke opnås dagligt. Testeren kigger gentagne gange på dokumentet og stiller vanskelige spørgsmål fra dag ét. Fordi testeren tænker anderledes, tænker han ikke, "Hvad vil softwaren gøre?" men "Hvad kan gå galt? Hvordan kan jeg bryde det?" Udvikleren tænker mere som kravingeniøren; han vil vide, "Hvad skal softwaren gøre?"

Testeren begynder at skrive sine prøver tidligt, når kravene ikke engang er udført endnu. Og han skriver dem i kravene. Testene bliver ikke kun en del af kravene, men også gennemgangs- / acceptprocessen for kravene, hvilket har nogle vigtige fordele:

  • Kunden får også tænke på testene. Normalt bliver hun endda involveret i at skabe dem (du kan blive overrasket over, hvor meget sjov hun kan have med dette).
  • Specifikationen bliver meget mere detaljeret og præcis, da testene normalt er mere præcise end blot tekst.
  • At tænke tidligt på reelle scenarier, levere testdata og beregne eksempler resulterer i et meget klarere billede af softwaren - som en prototype, kun med flere funktioner.

Endelig overføres kravene til udvikleren. Han har et lettere job nu, da specifikationen er mindre tilbøjelig til at ændre sig og på grund af alle de inkluderede eksempler. Lad os se på, hvordan denne proces letter en udviklers job.

Implementering af test-først

Normalt er den sværeste del af at starte test-første udvikling, at ingen ønsker at bruge så meget tid på at skrive tests, kun derefter for at finde en måde at få dem til at fungere. Ved hjælp af den ovenfor beskrevne proces modtager udvikleren funktionstestene som en del af sin kontrakt. Hans opgaver skifter fra "Byg det, jeg ønsker, og du er færdig, indtil jeg undersøger dit arbejde og foretager ændringer" til "Få disse tests til at fungere, og du er færdig." Nu har alle en bedre idé om, hvad de skal gøre, hvornår arbejdet skal afsluttes, og hvor projektet står.

Ikke alle disse tests vil blive automatiseret, og ikke alle vil være enhedstests. Vi deler normalt test i følgende kategorier (detaljer følger):

  • Datadrevne tests, der skal implementeres som enhedstest. Beregninger er det typiske eksempel.
  • Søgeordsstyrede tests, der automatiserer applikationsbrug. Dette er systemtest og kræver, at applikationen kører. Knapper klikkes, data indtastes, og resulterende sider / skærmbilleder kontrolleres for at indeholde bestemte værdier. Testteamet implementerer normalt disse tests, men nogle udviklere drager også fordel af dem.
  • Manuel test. Disse tests er enten for dyre til at automatisere, og de mulige fejl er ikke alvorlige nok, eller de er så grundlæggende (startsiden vises ikke), at deres brud ville blive opdaget med det samme.

Da jeg først læste om FitNesse i 2004, lo jeg og sagde, at det aldrig ville fungere. Ideen om at skrive mine tests til en wiki, der automatisk gjorde dem til tests, syntes for absurd. Det viste sig, jeg tog fejl. FitNesse er virkelig så simpelt som det ser ud til at være.

Denne enkelhed starter med installationen. Download den fulde distribution af FitNesse, og pak den ud. I den følgende diskussion antager jeg, at du har pakket distributionen til C: \ fitnesse.

Start FitNesse ved at køre run.bat (run.sh på Linux) script i C: \ fitnesse. Som standard kører FitNesse en webserver på port 80, men du kan angive en anden port, f.eks. 81, ved at tilføje -p 81 til første linje i batchfilen. Det er alt der er ved det; du kan nu få adgang til FitNesse på // localhost: 81.

I denne artikel bruger jeg Java-versionen af ​​FitNesse på Windows. Eksemplerne kan dog også bruges til andre versioner (Python, .Net) og platforme.

Nogle tests

FitNesses online dokumentation giver nogle enkle eksempler (sammenlignet med det berygtede pengeeksempel fra JUnit) for at komme i gang. De er fine til at lære at bruge FitNesse, men de er ikke tilstrækkeligt komplicerede til at overtale nogle skeptikere. Derfor vil jeg bruge et eksempel fra den virkelige verden fra et af mine seneste projekter. Jeg har forenklet problemet betydeligt, og koden, ikke taget direkte fra projektet, blev skrevet til illustrative formål. Alligevel skal dette eksempel være tilstrækkeligt kompliceret til at demonstrere kraften i FitNesses enkelhed.

Lad os antage, at vi arbejder på et projekt, der implementerer en kompleks Java-baseret virksomhedssoftware til et stort forsikringsselskab. Produktet håndterer hele virksomhedens forretning inklusive kunde- og kontraktstyring og betalinger. For vores eksempel vil vi se på en lille del af denne applikation.

I Schweiz har forældre ret til et børnetilskud pr. Barn. De modtager kun denne godtgørelse, hvis visse omstændigheder er opfyldt, og beløbet varierer. Følgende er en forenklet version af dette krav. Vi starter med "traditionelle" krav og flytter dem til FitNesse bagefter.

Der findes flere faser af børnetilskud. Kravet starter den første dag i den måned, barnet er født, og slutter den sidste dag i den måned, barnet når aldersgrænsen, afslutter sin lærlingeuddannelse eller dør.

Ved 12 års alderen hæves kravet til 190 CHF (Schweiz officielle valutasymbol) startende den første dag i fødselsmåneden.

Fuldtids- og deltidsansættelse af forældre fører til forskellige krav, som beskrevet i figur 1.

Den aktuelle beskæftigelsesgrad beregnes på de aktive arbejdskontrakter. Kontrakten skal være gyldig, og hvis en slutdato er indstillet, skal den placeres i "aktiveringsperioden". Figur 2 viser, hvor mange penge en forælder har ret til afhængigt af barnets alder.

Reglerne for disse betalinger tilpasses hvert andet år.

Ved førstebehandling lyder specifikationen muligvis nøjagtigt, og en udvikler skal være i stand til nemt at implementere den. Men er vi virkelig sikre på grænsebetingelserne? Hvordan ville vi teste disse krav?

Grænseforhold
Grænseforhold er situationerne direkte på, over og under kanterne af input- og outputækvivalensklasser. Erfaringer viser, at testsager, der udforsker randbetingelser, har en højere udbytte end testsager, der ikke gør det. Et typisk eksempel er den berygtede "engangs" på sløjfer og arrays.

Scenarier kan være en stor hjælp til at finde undtagelser og randbetingelser, da de giver en god måde at få domæneeksperter til at tale om forretning.

Scenarier

For de fleste projekter overleverer kravteknikeren specifikationen til udvikleren, der studerer kravene, stiller nogle spørgsmål og begynder at designe / kode / teste. Derefter afleverer udvikleren softwaren til testteamet og videregiver den efter nogle omarbejdninger og rettelser til kunden (som sandsynligvis vil tænke på nogle undtagelser, der kræver ændringer). Flytning af teksten til FitNesse ændrer ikke denne proces; dog tilføjer eksempler, scenarier og tests.

Scenarier er især nyttige til at få bolden til at rulle under testningen. Nogle eksempler følger. At besvare spørgsmålet om, hvor meget børnetilskud skal udbetales til hver, vil afklare meget:

  • Maria er enlig forælder. Hun har to sønner (Bob, 2 og Peter, 15) og arbejder deltid (20 timer om ugen) som sekretær.
  • Maria mister sit job. Senere arbejder hun 10 timer om ugen som butiksassistent og yderligere 5 timer som babysitter.
  • Paul og Lara har en datter (Lisa, 17), der er fysisk udfordret, og en søn (Frank, 18), der stadig er på universitetet.

Bare at tale gennem disse scenarier skal hjælpe testprocessen. At udføre dem manuelt på softwaren vil næsten helt sikkert finde nogle løse ender. Tror vi ikke kan gøre det, da vi endnu ikke har en prototype? Hvorfor ikke?

Søgeordsdrevet test

Keyword-driven test kan bruges til at simulere en prototype. FitNesse giver os mulighed for at definere søgeordsdrevne tests (se "Helt datadrevet automatiseret test" for detaljer). Selv uden software til (automatisk) at udføre testene mod, vil nøgleordsdrevne tests hjælpe meget.

Figur 3 viser, hvordan en søgeordsdrevet test kan se ud. Den første kolonne repræsenterer nøgleord fra FitNesse. Den anden kolonne repræsenterer metoder i en Java-klasse (vi skriver dem, og de skal følge de begrænsninger, der er lagt på metodenavne i Java). Den tredje kolonne repræsenterer data, der er indtastet i metoden fra den anden kolonne. Den sidste række viser, hvordan en mislykket test kan se ud (beståede tests er grønne). Som du kan se, er det ret nemt at finde ud af, hvad der gik galt.

Sådanne tests er lette og endda sjove at skabe. Testere uden programmeringsevner kan oprette dem, og kunden kan læse dem (efter en kort introduktion).

Definition af tests på denne måde lige ved siden af ​​kravet har nogle vigtige fordele i forhold til den traditionelle definition af testsager, selv uden automatisering:

  • Konteksten er lige ved hånden. Selve testsagen kan skrives med mindst mulig arbejde og er stadig præcis.
  • Hvis kravet ændres, er der en stor chance for, at testen også ændres (ikke meget sandsynligt, når flere værktøjer bruges).
  • Testen kan udføres med det samme for at vise, hvad der skal rettes for at få dette nye / ændrede krav til at fungere.

For at automatisere testen oprettes et tyndt lag af software, som delegeres til den virkelige testkode. Disse tests er især nyttige til automatisering af manuelle GUI-tests. Jeg udviklede en testramme baseret på HTTPUnit til automatisering af test af websider.

Her er koden, der automatisk udføres af FitNesse:

pakke stephanwiesner.javaworld;

import fit.ColumnFixture;

offentlig klasse ChildAllowanceFixture udvider ColumnFixture {public void personButton () {System.out.println ("trykke på person-knap"); } public void securityNumber (int number) {System.out.println ("indtastning af securityNumber" + nummer); } public int childAllowance () {System.out.println ("beregning af børnetilskud"); returnere 190; } [...]}

Resultatet af testene kan også undersøges i FitNesse (se figur 4), hvilket i høj grad hjælper med fejlretning. I modsætning til JUnit, hvor man afskrækkes fra at skrive fejlretningsmeddelelser, finder jeg dem absolut nødvendige, når jeg arbejder med automatiserede webtest.

Når du tester et webbaseret program, inkluderes fejlsider på FitNesse-siden og vises, hvilket gør fejlretning meget lettere end at arbejde med logfiler.

Datadrevet test

Mens søgeordsdrevet test er fint til GUI-automatisering, er datadrevet test det første valg til testkode, der foretager nogen form for beregning. Hvis du har skrevet enhedstest før, hvad er den mest irriterende ting ved disse tests? Chancerne er, du tænker på data. Dine tests vil være fulde af data, som ofte ændres, hvilket gør vedligeholdelse til et mareridt. Test af forskellige kombinationer kræver forskellige data, hvilket sandsynligvis gør dine tests komplicerede, grimme dyr.