Programmering

Test-første utvikling med FitNesse

I løpet av de siste årene har jeg jobbet i alle roller i testprosessen, ved hjelp av JavaScript, Perl, PHP, Struts, Swing og modelldrevne arkitekturer på serversiden. Alle prosjekter var forskjellige, men de hadde alle noen fellestrekk: fristene gikk for sent, og prosjektene hadde vanskeligheter med å oppnå det kunden egentlig behov for.

Hvert prosjekt hadde noen form for krav, noen var veldig detaljerte, noen, bare noen få sider lange. Disse kravene gjennomgikk vanligvis tre faser:

  • De ble skrevet (enten av kunden eller av entreprenøren) og mottok en slags offisiell aksept
  • Testerne prøvde å jobbe med kravene og fant dem mer eller mindre utilstrekkelige
  • Prosjektet gikk inn i en fase med akseptantesting, og kunden husket plutselig alle slags ting programvaren trengte for å gjøre i tillegg / annerledes

Den siste fasen førte til endringer, som førte til tapte tidsfrister, som satte stress på utviklerne, som igjen førte til flere feil. Feilantallet begynte å øke raskt, og den generelle kvaliteten på systemet gikk ned. Høres kjent ut?

La oss vurdere hva som gikk galt i prosjektene beskrevet ovenfor: Kunden, utvikleren og testeren jobbet ikke sammen; de ga kravene videre, men hver rolle hadde forskjellige behov. I tillegg utviklet utviklerne vanligvis en slags automatiserte tester, og testerne prøvde å automatisere noen tester også. Vanligvis kunne de ikke koordinere testingen tilstrekkelig, og mange ting ble testet to ganger, mens andre (vanligvis de harde delene), ikke i det hele tatt. Og kunden så ingen tester. Denne artikkelen beskriver en måte å løse disse problemene på ved å kombinere krav med automatiserte tester.

Gå inn på FitNesse

FitNesse er en wiki med noen tilleggsfunksjoner for å utløse JUnit-tester. Hvis disse testene kombineres med krav, tjener de som konkrete eksempler, og gjør kravene enda tydeligere. Videre er testdataene logisk organisert. Det viktigste med å bruke FitNesse er imidlertid idé bak det, noe som betyr at kravene viser seg å være skrevet (delvis) som tester, noe som gjør dem testbare og derfor kan oppfyllelsen kontrolleres.

Ved å bruke FitNesse kan utviklingsprosessen se slik ut: Kravingeniøren skriver kravene i FitNesse (i stedet for Word). Han prøver å få kunden med så mye som mulig, men det kan vanligvis ikke oppnås på daglig basis. Testeren titter på dokumentet gjentatte ganger og stiller vanskelige spørsmål fra første dag. Fordi testeren tenker annerledes, tenker han ikke: "Hva vil programvaren gjøre?" men "Hva kan gå galt? Hvordan kan jeg bryte det?" Utvikleren tenker mer som kravingeniøren; han vil vite, "Hva må programvaren gjøre?"

Testeren begynner å skrive testene sine tidlig, når kravene ikke en gang er gjort ennå. Og han skriver dem inn i kravene. Testene blir en del av ikke bare kravene, men også gjennomgangs- / akseptprosessen for kravene, som har noen viktige fordeler:

  • Kunden får tenke på testene også. Vanligvis blir hun til og med involvert i å lage dem (du kan bli overrasket over hvor mye moro hun kan ha med dette).
  • Spesifikasjonen blir mye mer detaljert og presis, ettersom testene vanligvis er mer presise enn bare tekst.
  • Å tenke tidlig på virkelige scenarier, levere testdata og beregne eksempler resulterer i et mye klarere syn på programvaren - som en prototype, bare med flere funksjoner.

Til slutt blir kravene overlevert til utvikleren. Han har en lettere jobb nå, da spesifikasjonen er mindre sannsynlig å endres og på grunn av alle eksemplene som følger med. La oss se på hvordan denne prosessen gjør utviklerens jobb lettere.

Implementering av test-først

Vanligvis er den vanskeligste delen av å starte test-første utvikling at ingen ønsker å bruke så mye tid på å skrive tester, bare da for å finne en måte å få dem til å fungere. Ved hjelp av prosessen beskrevet ovenfor mottar utvikleren funksjonstestene som en del av kontrakten. Oppgavene hans endres fra "Bygg det jeg vil ha, og du er ferdig, til jeg undersøker arbeidet ditt og gjør endringer" til "Få disse testene til å fungere, og du er ferdig." Nå har alle en bedre ide om hva de skal gjøre, når arbeidet skal fullføres, og hvor prosjektet står.

Ikke alle disse testene vil bli automatisert, og ikke alle vil være enhetstester. Vi deler vanligvis tester i følgende kategorier (detaljer følger):

  • Datadrevne tester som må implementeres som enhetstester. Beregninger er det typiske eksemplet.
  • Søkeorddrevne tester som automatiserer applikasjonsbruk. Dette er systemtester og krever at applikasjonen kjører. Knapper klikkes, data blir skrevet inn, og resulterende sider / skjermer blir sjekket for å inneholde visse verdier. Testteamet implementerer vanligvis disse testene, men noen utviklere drar nytte av dem også.
  • Manuelle tester. Disse testene er enten for dyre å automatisere og de mulige feilene ikke alvorlige nok, eller de er så grunnleggende (startsiden vises ikke) at bruddet deres vil bli oppdaget med en gang.

Da jeg først leste om FitNesse i 2004, lo jeg og sa at det aldri ville fungere. Ideen om å skrive testene mine til en wiki som gjorde dem automatisk til tester, virket for absurd. Det viste seg at jeg tok feil. FitNesse er virkelig så enkelt som det ser ut til å være.

Denne enkelheten starter med installasjonen. Bare last ned full distribusjon av FitNesse og pakk den ut. I den følgende diskusjonen antar jeg at du har pakket ut distribusjonen til C: \ fitnesse.

Start FitNesse ved å kjøre run.bat (run.sh på Linux) skript i C: \ fitnesse. Som standard kjører FitNesse en webserver på port 80, men du kan spesifisere en annen port, si 81, ved å legge til -p 81 til første linje i batchfilen. Det er alt det er til det; du kan nå få tilgang til FitNesse på // localhost: 81.

I denne artikkelen bruker jeg Java-versjonen av FitNesse på Windows. Eksemplene kan imidlertid også brukes til andre versjoner (Python, .Net) og plattformer.

Noen tester

FitNesses online dokumentasjon gir noen enkle eksempler (som kan sammenlignes med det beryktede pengeeksemplet fra JUnit) for å komme i gang. De er fine for å lære å bruke FitNesse, men de er ikke tilstrekkelig kompliserte til å overtale noen skeptikere. Derfor vil jeg bruke et eksempel fra den virkelige verden fra et av mine nylige prosjekter. Jeg har forenklet problemet betydelig, og koden, ikke hentet direkte fra prosjektet, ble skrevet for illustrative formål. Likevel bør dette eksemplet være tilstrekkelig komplisert til å demonstrere kraften i FitNesses enkelhet.

La oss anta at vi jobber med et prosjekt som implementerer en kompleks Java-basert programvare for et stort forsikringsselskap. Produktet vil håndtere selskapets hele virksomhet, inkludert kunde- og kontraktstyring og betalinger. For vårt eksempel vil vi se på en liten del av denne applikasjonen.

I Sveits har foreldre rett til ett barnetillegg per barn. De får bare denne godtgjørelsen hvis visse omstendigheter er oppfylt, og beløpet varierer. Følgende er en forenklet versjon av dette kravet. Vi vil starte med "tradisjonelle" krav og flytte dem til FitNesse etterpå.

Flere faser av barnetillegg eksisterer. Kravet starter den første dagen i den måneden barnet blir født og slutter den siste dagen i den måneden barnet når aldersgrensen, avslutter læretiden eller dør.

Ved fylte 12 år heves kravet til 190 CHF (Sveits offisielle valutasymbol) fra første dag i fødselsmåneden.

Heltids- og deltidsansettelse av foreldre fører til forskjellige krav, som beskrevet i figur 1.

Nåværende sysselsettingsgrad beregnes på de aktive arbeidskontraktene. Kontrakten må være gyldig, og hvis en sluttdato er angitt, må den ligge i "aktiveringsperioden". Figur 2 viser hvor mye penger en forelder har krav på, avhengig av barnets alder.

Regelverket som regulerer disse betalingene tilpasses hvert annet år.

Ved første lesing kan spesifikasjonen høres nøyaktig ut, og en utvikler bør være i stand til å implementere den enkelt. Men er vi virkelig sikre på grensevilkårene? Hvordan vil vi teste disse kravene?

Grensebetingelser
Grenseforhold er situasjonene direkte på, over og under kantene til inngangs- og utgangsekvivalensklasser. Erfaringer viser at testsaker som utforsker grensevilkår, har høyere utbytte enn testsaker som ikke gjør det. Et typisk eksempel er den beryktede "engangs" på løkker og matriser.

Scenarier kan være til stor hjelp for å finne unntak og grensevilkår, ettersom de gir en god måte å få domeneksperter til å snakke om virksomheten.

Scenarier

For de fleste prosjekter gir kravingeniøren spesifikasjonen til utvikleren, som studerer kravene, stiller noen spørsmål og begynner å designe / kode / teste. Etterpå overleverer utvikleren programvaren til testteamet, og etter noen omarbeid og reparasjoner viderefører den den til kunden (som sannsynligvis vil tenke på noen unntak som krever endringer). Å flytte teksten til FitNesse vil ikke endre denne prosessen; imidlertid å legge til eksempler, scenarier og tester vil.

Scenarier er spesielt nyttige for å få ballen til å rulle under testing. Noen eksempler følger. Å svare på spørsmålet om hvor mye barnetillegg skal utbetales til hver vil avklare mye:

  • Maria er aleneforelder. Hun har to sønner (Bob, 2 og Peter, 15) og jobber deltid (20 timer per uke) som sekretær.
  • Maria mister jobben. Senere jobber hun 10 timer i uken som butikkassistent og ytterligere 5 timer som barnevakt.
  • Paul og Lara har en datter (Lisa, 17) som er fysisk utfordret og en sønn (Frank, 18) som fortsatt er på universitetet.

Bare det å snakke gjennom disse scenariene, kan hjelpe testprosessen. Å utføre dem manuelt på programvaren vil nesten helt sikkert finne noen løse ender. Tror vi ikke kan gjøre det, siden vi ikke har en prototype ennå? Hvorfor ikke?

Søkeorddrevet testing

Søkeorddrevet testing kan brukes til å simulere en prototype. FitNesse lar oss definere søkeorddrevne tester (se "Totally Data-Driven Automated Testing" for detaljer). Selv uten programvare som (automatisk) kan utføre testene mot, vil søkeorddrevne tester hjelpe mye.

Figur 3 viser hvordan en søkeorddrevet test kan se ut. Den første kolonnen representerer nøkkelord fra FitNesse. Den andre kolonnen representerer metoder i en Java-klasse (vi skriver dem, og de må følge begrensningene som er satt for metodenavn i Java). Den tredje kolonnen representerer data som er lagt inn i metoden fra den andre kolonnen. Den siste raden viser hvordan en mislykket test kan se ut (beståtte tester er grønne). Som du ser er det ganske enkelt å finne ut hva som gikk galt.

Slike tester er enkle og til og med morsomme å lage. Testere uten programmeringsferdigheter kan lage dem, og kunden kan lese dem (etter en kort introduksjon).

Å definere tester på denne måten, rett ved siden av kravet, har noen viktige fordeler i forhold til den tradisjonelle definisjonen av testsaker, selv uten automatisering:

  • Konteksten er for hånden. Selve testsaken kan skrives med minst mulig arbeid og er fremdeles presis.
  • Hvis kravet endres, er det stor sjanse for at testen også endres (ikke veldig sannsynlig når flere verktøy brukes).
  • Testen kan utføres på en gang for å vise hva som må fikses for å få dette nye / endrede kravet til å fungere.

For å automatisere testen opprettes et tynt lag med programvare som delegeres til den virkelige testkoden. Disse testene er spesielt nyttige for automatisering av manuelle GUI-tester. Jeg utviklet et testrammeverk basert på HTTPUnit for å automatisere testing av websider.

Her er koden automatisk utført av FitNesse:

pakke stephanwiesner.javaworld;

import fit.ColumnFixture;

offentlig klasse ChildAllowanceFixture utvider ColumnFixture {public void personButton () {System.out.println ("trykke på person-knapp"); } public void securityNumber (int number) {System.out.println ("enter securityNumber" + number); } public int childAllowance () {System.out.println ("beregning av barnetrygd"); retur 190; } [...]}

Resultatet av testene kan også undersøkes i FitNesse (se figur 4), noe som i stor grad hjelper med feilsøking. I motsetning til JUnit, der man er motløs fra å skrive feilsøkingsmeldinger, finner jeg dem helt nødvendige når man jobber med automatiserte webtester.

Når du tester et nettbasert program, inkluderes feilsider på FitNesse-siden og vises, noe som gjør feilsøking mye enklere enn å jobbe med loggfiler.

Datadrevet testing

Mens søkeorddrevet testing er bra for GUI-automatisering, er datadrevet testing førstevalget for testing av kode som gjør noen form for beregning. Hvis du har skrevet enhetstester før, hva er det som er mest irriterende med disse testene? Sjansen er, du tenker på data. Testene dine vil være fulle av data, som ofte endres og gjør vedlikehold til et mareritt. Å teste forskjellige kombinasjoner krever forskjellige data, noe som sannsynligvis gjør testene dine kompliserte, stygge dyr.

$config[zx-auto] not found$config[zx-overlay] not found