Programmering

Test webapplikasjoner med HttpUnit

I en typisk bedriftsapplikasjon krever mange områder testing. Med utgangspunkt i de enkleste komponentene, klassene, må utviklerne eller spesialiserte testutviklere programmere enhetstester for å sikre at applikasjonens minste enheter oppfører seg riktig. Hver komponent kan potensielt bestå enhetstestene alene; utviklere må imidlertid sørge for at de jobber sammen som forventet - som en del av et delsystem, og som en del av hele applikasjonen - derav, integrasjonstester må utføres. I noen prosjekter må ytelseskravene oppfylles, slik at kvalitetssikringsingeniørene utfører lastetester for å verifisere og dokumentere hvordan applikasjonen presterer under forskjellige forhold. Under applikasjonsutviklingen utfører kvalitetssikringsingeniører automatiserte og manuelle funksjonstester for å teste applikasjonens atferd fra brukerens synspunkt. Når et utviklingsprosjekt nesten fullfører en bestemt milepæl, aksept tester kan utføres for å verifisere at søknaden oppfyller kravene.

HttpUnit er et rammeverk basert på JUnit, som tillater implementering av automatiserte testskripter for webapplikasjoner. Det er best egnet for implementering av automatiserte funksjonstester eller akseptatestester. Som navnet antyder, kan den brukes til enhetstesting; typiske weblagskomponenter som JSP (JavaServer Pages) -sider, servlets og andre malkomponenter egner seg imidlertid ikke til enhetstesting. Når det gjelder forskjellige MVC (Model-View Controller) rammebaserte komponenter, er disse bedre egnet for testing med andre testrammer. Struts-handlinger kan enhetstestes med StrutsUnit, og WebWork 2-handlinger kan for eksempel enhetstestes uten en webcontainer.

Test mål

Før vi hopper inn i arkitekturen og implementeringsdetaljene, er det viktig å avklare nøyaktig hva testskriptene trenger for å bevise om nettapplikasjonen. Det er mulig å bare simulere oppførselen til en tilfeldig besøkende på nettstedet ved å klikke på interessante lenker og lese sider i tilfeldig rekkefølge, men resultatet av disse tilfeldige skriptene vil ikke beskrive programmets fullstendighet og kvalitet.

En typisk bedriftens webapplikasjon (eller et komplekst nettsted) har flere dokumenter som beskriver kravene til de forskjellige brukerne eller applikasjonsansvarlige. Disse kan omfatte spesifikasjoner for brukstilfeller, ikke-funksjonelle kravspesifikasjoner, testkofferspesifikasjoner avledet fra andre gjenstander, designdokumenter for brukergrensesnitt, mockups, aktørprofiler og forskjellige andre gjenstander. For en enkel applikasjon kan hele spesifikasjonen muligens bestå av en enkel tekstfil med en liste over krav.

Fra disse dokumentene må vi lage en organisert liste over testsaker. Hver testtilfelle beskriver et scenario som kan oppnås av en besøkende via en nettleser. En god praksis er å sikte på like store scenarier - større scenarier kan brytes ned til mindre biter. Mange gode bøker og artikler diskuterer etableringen av test-case spesifikasjoner. La oss anta at du har et sett med elementer du vil teste for webapplikasjonen din, organisert i sett med test-case-scenarier.

På tide å laste ned ting!

Ok, nå vet vi de kjedelige greiene, la oss laste ned noen kule leker! Først og fremst trenger vi en installert Java 2 SDK for å kompilere og utføre testene våre. Deretter må vi laste ned HttpUnit-rammeverket - for øyeblikket på versjon 1.5.5. Binærpakken inneholder alle nødvendige tredjepartsbiblioteker. Vi trenger også Ant build-verktøyet for å kjøre testene og generere rapporter automatisk. Enhver ganske fersk versjon av disse verktøyene vil sannsynligvis fungere; Jeg foretrekker bare å bruke den nyeste og beste versjonen av alt.

For å skrive og utføre tester anbefaler jeg å bruke en IDE som har en innebygd JUnit testløper. Jeg bruker Eclipse 3.0M7 for å utvikle testskriptene mine, men IntelliJ har også JUnit-støtte, i likhet med de nylig utgitte IDE-ene.

HttpUnit: HTTP-klientsimulatoren

Ettersom vi ønsker å teste webapplikasjoner, ideelt sett, bør testverktøyet oppføre seg nøyaktig som brukernes nettlesere. Vår applikasjon (testmålet) skal ikke være klar over noen forskjell når vi serverer sider til en nettleser eller testverktøyet. Det er akkurat det HttpUnit gir: det simulerer en vanlig nettlesers GET- og POST-forespørsler, og gir en fin objektmodell som du kan kode testene våre mot.

Sjekk ut den detaljerte API-guiden for resten av klassene og metodene; Figur 1 gir bare en kort oversikt over klassene jeg bruker hyppigst. En brukersession (en sekvens av interaksjoner med webapplikasjonen) er innkapslet med en Webkonversasjon. Vi konstruerer Webforespørsels, vanligvis konfigurerer URL og parametere, og deretter sender vi den ned gjennom Webkonversasjon. Rammeverket returnerer deretter a WebResponse, som inneholder den returnerte siden og attributter fra serveren.

Her er et eksempel på en HttpUnit-testsak fra HttpUnit-dokumentene:

 / ** * Bekrefter at innlevering av påloggingsskjemaet med navnet "master" resulterer * i en side som inneholder teksten "Topphemmelig" ** / public void testGoodLogin () kaster Unntak {WebConversation samtale = ny WebConversation (); WebRequest-forespørsel = ny GetMethodWebRequest ("//www.meterware.com/servlet/TopSecret"); WebResponse respons = samtale.getResponse (forespørsel); WebForm loginForm = respons.getForms () [0]; forespørsel = loginForm.getRequest (); request.setParameter ("navn", "master"); respons = convers.getResponse (forespørsel); assertTrue ("Pålogging aksepteres ikke", response.getText (). indexOf ("Du har gjort det!")! = -1); assertEquals ("Sidetittel", "Topphemmelig", respons.getTitle ()); } 

Arkitektoniske hensyn

Legg merke til hvordan Java-eksemplet ovenfor inneholder domenenavnet til serveren som kjører applikasjonen. Under utviklingen av et nytt system lever applikasjonen på flere servere, og serverne kan kjøre flere versjoner. Det er åpenbart en dårlig idé å beholde servernavnet i Java-implementeringen - for hver nye server må vi kompilere kildene våre på nytt. Andre gjenstander skal ikke ligge i kildefilene, som brukernavn og passord, som skal være konfigurerbar for den spesifikke distribusjonen. På den annen side bør vi ikke overarkitektur en enkel test-case implementering. Normalt inneholder testspesifikasjonene allerede det meste av systemtilstanden og spesifikke parameterbeskrivelser for vårt scenario, så det er det ikke noe poeng å gjøre alt parametrerbart i implementeringen.

Under kodingen vil du innse at mange kodeseksjoner vises i mer enn en test-case-implementering (potensielt i alle test tilfeller). Hvis du er en erfaren objektorientert utvikler, vil du bli fristet til å lage klassehierarkier og vanlige klasser. I noen tilfeller er det veldig fornuftig - for eksempel skal innloggingsprosedyren være en vanlig metode som er tilgjengelig for alle testsaker. Du må imidlertid trekke deg litt tilbake og innse at du ikke bygger et nytt produksjonssystem på toppen av mål-for-test-applikasjonen - disse Java-klassene er ikke mer enn testskript for å validere nettstedets utdata. Tren sunn fornuft og sikte på enkle, sekvensielle og selvstendige testskripter.

Testtilfellene er vanligvis skjøre. Hvis en utvikler endrer en URL, omorganiserer du layoutet

struktur, eller endrer ID for et skjemaelement, vil den besøkende sannsynligvis ikke se noen forskjell, men testskriptene dine vil bli blåst. Forvent mye omarbeid og endring for hver implementering av testsaker. Objektorientert design kan redusere arbeidet med å omarbeide vanlige deler i testtilfellene, men fra et kvalitetssikringsingeniør eller tester er jeg sikker på at en enkelt, sekvensielt skript som samhandler med et nettsted er lettere å vedlikeholde og fikse.

Sporbarhet er avgjørende for testsakene våre. Hvis noe går KA-BOOM, eller for eksempel et beregningsresultat er galt, er det viktig å peke utvikleren til den tilsvarende test-case-spesifikasjonen og use-case-spesifikasjonen for en rask feiloppløsning. Merk derfor implementeringen din med referanser til de originale spesifikasjonsdokumentene. Å inkludere versjonsnummeret på disse dokumentene er også nyttig. Det kan bare være en enkel kodekommentar eller en kompleks mekanisme der selve testrapportene lenker til dokumentene; det viktigste er å ha referansen i koden og til holde sporbarheten.

Når får jeg skrive kode?

Nå som du er klar over kravene (bruksdokumenter og tilhørende testsaksspesifikasjoner), forstår rammeverket og har et sett med arkitektoniske retningslinjer, la oss komme i gang.

For utviklingen av test-case-implementeringene, foretrekker jeg å jobbe i Eclipse. Først og fremst har den en fin JUnit-testløper. Du kan velge en Java-klasse, og fra Run-menyen kan du kjøre den som en JUnit-enhetstest. Løperen viser listen over anerkjente testmetoder og utførelsesresultatet. Når alt går greit under testkjøringen, gir det en fin grønn linje. Hvis det oppstod et unntak eller en påstandssvikt, viser den en foruroligende rød linje. Jeg tror den visuelle tilbakemeldingen er veldig viktig - den gir en følelse av prestasjon, spesielt når du skriver enhetstester for din egen kode. Jeg liker også å bruke Eclipse for refactoring-evner. Hvis jeg er klar over at jeg i en test-case-klasse må kopiere og lime inn kodeseksjoner, kan jeg bare bruke Refactoring-menyen til å lage en metode fra kodeseksjonen i stedet. Hvis jeg innser at mange testtilfeller vil bruke samme metode, kan jeg bruke menyen til å trekke metoden min inn i basisklassen min.

Basert på de arkitektoniske kravene ovenfor, oppretter jeg for hvert prosjekt vanligvis en basestest-case-klasse som utvider JUnit Testforsøk klasse. Jeg kaller det ConfigurableTestCase. Hver implementering av testtilfeller utvider denne klassen, se figur 2.

ConfigurableTestCase inneholder vanligvis de vanlige metodene og initialiseringskoden for testsaken. Jeg bruker en eiendomsfil til å lagre servernavnet, applikasjonskonteksten, forskjellige påloggingsnavn for hver rolle og noen ekstra innstillinger.

De spesifikke test-case-implementeringene inneholder en testmetode per test-case scenario (fra test-case spesifikasjonsdokumentet). Hver metode logger vanligvis på med en bestemt rolle og utfører deretter interaksjonen med webapplikasjonen. De fleste testsaker trenger ikke en spesifikk bruker for å utføre aktivitetene; de krever vanligvis en bruker i en bestemt rolle, som administrator, besøkende eller registrert bruker. Jeg lager alltid en LoginMode enum, som inneholder de tilgjengelige rollene. Jeg bruker Jakarta Commons ValuedEnum-pakken for å lage enums for rollene. Når en spesifikk testmetode i en testtilfelle implementering logger på, må den spesifisere hvilken påloggingsrolle som kreves for det aktuelle testscenariet. Muligheten for å logge på med en bestemt bruker bør selvfølgelig også være mulig, for eksempel å verifisere brukssaken til registrert bruker.

Etter hver forespørsel og svarsyklus må vi vanligvis kontrollere om den returnerte siden inneholder en feil, og vi må bekrefte påstandene våre om hvilket innhold svaret skal inneholde. Vi må være forsiktige også her; vi bør bare verifisere elementer som ikke er variable og ikke for skjøre i applikasjonen. For eksempel, hvis vi hevder spesifikke sidetitler, vil testene våre sannsynligvis ikke kjøres hvis språket kan velges i applikasjonen, og vi ønsker å bekrefte en annen språkutrulling. På samme måte er det lite poeng i å sjekke et element på siden basert på plassering i et bordoppsett; tabellbaserte design endres ofte, så vi bør forsøke å identifisere elementer basert på deres ID. Hvis noen viktige elementer på siden ikke har ID eller navn, bør vi bare be utviklerne om å legge dem til, i stedet for å prøve å omgå dem.

Påstander fra JUnit gir en dårlig tilnærming for å sjekke om utseendet, utformingen og sideutformingen oppfyller kravene. Det er mulig, gitt uendelig lang tid for testutviklingen, men en god menneskelig tester kan vurdere disse tingene mer effektivt. Så konsentrer deg om å verifisere webapplikasjonens funksjonalitet, i stedet for å sjekke alt mulig på siden.

Her er et oppdatert testscenario basert på vår test-case-arkitektur. Klassen strekker seg ConfigurableTestCase, og påloggingsinformasjonen håndteres i basisklassen:

 / ** * Bekrefter at innlevering av påloggingsskjemaet med navnet "master" resulterer * i en side som inneholder teksten "Topphemmelig" ** / public void testGoodLogin () kaster Unntak {WebConversation samtale = ny WebConversation (); WebResponse respons = pålogging (samtale, LoginMode.ADMIN_MODE); assertTrue ("Pålogging aksepteres ikke", response.getText (). indexOf ("Du har gjort det!")! = -1); assertEquals ("Sidetittel", "Topphemmelig", respons.getTitle ()); } 

Tips og triks

De fleste scenarier kan håndteres ganske enkelt ved å stille inn Webform parametere og deretter lete etter spesifikke elementer med resultater i WebResponse sider, men det er alltid noen utfordrende testsaker.

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