Programmering

Test webapplikationer med HttpUnit

I en typisk virksomhedsapplikation kræver mange områder test. Startende fra de enkleste komponenter, klasser, skal udviklerne eller specialiserede testudviklere programmere enhedstest for at sikre, at applikationens mindste enheder opfører sig korrekt. Hver komponent kan muligvis bestå enhedstestene alene; dog skal udviklere sikre, at de arbejder sammen som forventet - som en del af et undersystem og som en del af hele applikationen - derfor integrationstest skal udføres. I nogle projekter skal præstationskrav være opfyldt, så kvalitetssikringsingeniørerne udfører belastningstest at kontrollere og dokumentere, hvordan applikationen fungerer under forskellige forhold. Under applikationsudviklingen udfører kvalitetssikringsingeniører automatiseret og manuel funktionelle tests for at teste applikationens opførsel ud fra brugerens synspunkt. Når et udviklingsprojekt næsten gennemfører en bestemt milepæl, accept test kan udføres for at kontrollere, at ansøgningen opfyldte kravene.

HttpUnit er en ramme baseret på JUnit, som tillader implementering af automatiserede testskripts til webapplikationer. Det er bedst egnet til implementering af automatiserede funktionelle tests eller acceptstests. Som navnet antyder, kan det bruges til enhedstest; typiske weblagskomponenter som JSP (JavaServer Pages) sider, servlets og andre skabelonkomponenter egner sig imidlertid ikke til enhedstest. Hvad angår forskellige MVC (Model-View Controller) rammebaserede komponenter, er disse bedre egnet til test med andre testrammer. Struts handlinger kan enhedstestes med StrutsUnit, og WebWork 2 handlinger kan f.eks. Enhedstestes uden en webcontainer.

Test mål

Før vi hopper ind i arkitekturen og implementeringsdetaljerne, er det vigtigt at præcisere præcis, hvad testskripterne skal bruge til at bevise om webapplikationen. Det er muligt bare at simulere en afslappet webstedsbesøgendes opførsel ved blot at klikke på interessante links og læse sider i tilfældig rækkefølge, men resultatet af disse tilfældige scripts beskriver ikke applikationens fuldstændighed og kvalitet.

En typisk virksomhedswebapplikation (eller et komplekst websted) har flere dokumenter, der beskriver kravene fra de forskellige brugere eller applikationsvedligeholdere. Disse kan omfatte brugssagsspecifikationer, ikke-funktionelle kravspecifikationer, test-sagsspecifikationer afledt af de andre artefakter, brugergrænseflades designdokumenter, mockups, aktørprofiler og forskellige yderligere artefakter. For en simpel applikation kan hele specifikationen muligvis bestå af en simpel tekstfil med en liste over krav.

Fra disse dokumenter skal vi oprette en organiseret liste over testsager. Hver testsag beskriver et scenarie, der kan opnås af en webbesøgende via en webbrowser. En god praksis er at sigte mod scenarier af samme størrelse - større scenarier kan opdeles til mindre bidder. Mange fremragende bøger og artikler diskuterer oprettelsen af ​​test-case specifikationer. Lad os antage, at du i denne artikel har et sæt emner, du vil teste til din webapplikation, organiseret i sæt test-case-scenarier.

Tid til at downloade ting!

Okay, nu kender vi de kedelige ting, lad os downloade nogle seje legetøj! Først og fremmest har vi brug for en installeret Java 2 SDK til at kompilere og udføre vores tests. Så er vi nødt til at downloade HttpUnit-rammen - i øjeblikket i version 1.5.5. Den binære pakke indeholder alle de krævede tredjepartsbiblioteker. Vi har også brug for Ant-build-værktøjet til at køre testene og generere rapporter automatisk. Enhver ret ny version af disse værktøjer vil sandsynligvis fungere; Jeg foretrækker bare at bruge den nyeste og største version af alt.

For at skrive og udføre test anbefaler jeg at bruge en IDE, der har en indlejret JUnit-testløber. Jeg bruger Eclipse 3.0M7 til at udvikle mine testskripter, men IntelliJ har også JUnit-support, ligesom senest frigivne IDE'er.

HttpUnit: HTTP-klientsimulatoren

Da vi gerne vil teste webapplikationer, skal testværktøjet ideelt set opføre sig nøjagtigt som brugernes webbrowsere. Vores applikation (testmålet) bør ikke være opmærksom på nogen forskel, når vi viser sider til en webbrowser eller testværktøjet. Det er præcis, hvad HttpUnit giver: det simulerer en normal browsers GET- og POST-anmodninger og giver en flot objektmodel, som vores tests kan kodes mod.

Tjek den detaljerede API-guide for resten af ​​klasser og metoder; Figur 1 giver bare en kort oversigt over de klasser, jeg bruger hyppigst. En brugersession (en sekvens af interaktioner med webapplikationen) er indkapslet med en Webkonversation. Vi konstruerer Webforespørgsels, typisk konfiguration af URL og parametre, og derefter sender vi den ned gennem Webkonversation. Rammen returnerer derefter a WebResponse, der indeholder den returnerede side og attributter fra serveren.

Her er et eksempel på en HttpUnit-testsag fra HttpUnit-dokumenterne:

 / ** * Kontrollerer, at indsendelse af loginformularen med navnet "master" resulterer * i en side, der indeholder teksten "Top Secret" ** / public void testGoodLogin () kaster Undtagelse {WebConversation samtale = ny WebConversation (); WebRequest-anmodning = ny GetMethodWebRequest ("//www.meterware.com/servlet/TopSecret"); WebResponse svar = samtale.getResponse (anmodning); WebForm loginForm = respons.getForms () [0]; anmodning = loginForm.getRequest (); request.setParameter ("navn", "master"); respons = konversation.getResponse (anmodning); assertTrue ("Login accepteres ikke", response.getText (). indexOf ("Du klarede det!")! = -1); assertEquals ("Sidetitel", "Tophemmelighed", respons.getTitle ()); } 

Arkitektoniske overvejelser

Bemærk, hvordan Java-prøven ovenfor indeholder domænenavnet på den server, der kører applikationen. Under udviklingen af ​​et nyt system lever applikationen på flere servere, og serverne kan køre flere versioner. Det er åbenbart en dårlig idé at holde servernavnet i Java-implementeringen - for hver nye server er vi nødt til at kompilere vores kilder igen. Andre emner burde ikke findes i kildefilerne, som f.eks. Brugernavne og adgangskoder konfigurerbar til den specifikke implementering. På den anden side skal vi ikke overarkitekturere en simpel test-case-implementering. Normalt indeholder test-case-specifikationen allerede det meste af systemtilstanden og specifikke parameterbeskrivelser for vores scenario, så der er intet punkt at gøre alt parametriserbart i implementeringen.

Under kodning vil du indse, at mange kodeafsnit vises i mere end en test-case-implementering (potentielt i alle testsagerne). Hvis du er en erfaren objektorienteret udvikler, vil du blive fristet til at oprette klassehierarkier og almindelige klasser. I nogle tilfælde giver det meget mening - for eksempel skal loginproceduren være en almindelig metode til rådighed for alle testsager. Du skal dog træde lidt tilbage og indse, at du ikke bygger et nyt produktionssystem oven på target-of-test-applikationen - disse Java-klasser er ikke mere end test-scripts til at validere webstedets output. Udøv sund fornuft og sigter mod enkle, sekventielle og selvstændige testskripter.

Testcases er typisk skrøbelige. Hvis en udvikler ændrer en URL, omorganiserer layoutet

struktur eller ændrer et formelements ID, vil den besøgende sandsynligvis ikke se nogen forskel, men dine testskripter vilje blæses. Forvent en masse omarbejdning og ændringer for hver implementering af test-case. Objektorienteret design kunne reducere indsatsen for at omarbejde almindelige dele i testsagerne, men set fra en kvalitetssikringsingeniør eller testers perspektiv er jeg sikker på, at en simpelt, sekventielt script der interagerer med et websted er lettere at vedligeholde og rette.

Sporbarhed er afgørende for vores testsager. Hvis noget går KA-BOOM, eller f.eks. Et beregningsresultat er forkert, er det vigtigt at pege udvikleren på den tilsvarende test-case-specifikation og use-case-specifikationen for en hurtig fejlopløsning. Kommenter derfor din implementering med henvisninger til de originale specifikationsdokumenter. Det er også nyttigt at medtage versionsnummeret på disse dokumenter. Det kunne bare være en simpel kodekommentar eller en kompleks mekanisme, hvor selve testrapporterne linker til dokumenterne; det vigtige er at have henvisningen i koden og til bevar sporbarheden.

Hvornår skal jeg skrive kode?

Nu hvor du er opmærksom på kravene (use-case-dokumenter og tilsvarende test-case-specifikationer), forstår rammens grundlæggende egenskaber og har et sæt arkitektoniske retningslinjer, lad os komme i gang.

Til udviklingen af ​​test-case implementeringer foretrækker jeg at arbejde i Eclipse. Først og fremmest har den en dejlig JUnit-testløber. Du kan vælge en Java-klasse, og fra menuen Kør kan du køre den som en JUnit-enhedstest. Løberen viser listen over anerkendte testmetoder og udførelsesresultatet. Når alt går i orden under testkørslen, giver det en dejlig grøn linje. Hvis der opstod en undtagelse eller påstandsfejl, viser den en foruroligende rød linje. Jeg synes, den visuelle feedback er virkelig vigtig - den giver en følelse af opnåelse, især når du skriver enhedstest til din egen kode. Jeg kan også godt lide at bruge Eclipse til dets refactoring-muligheder. Hvis jeg er klar over, at jeg inden for en test-case-klasse skal kopiere og indsætte kodeafsnit, kan jeg bare bruge menuen Refactoring til at oprette en metode fra kodeafsnittet i stedet. Hvis jeg er klar over, at adskillige testsager bruger den samme metode, kan jeg bruge menuen til at trække min metode op i min basisklasse.

Baseret på de ovennævnte arkitektoniske krav opretter jeg typisk for hvert projekt en basistest-case-klasse, der udvider JUnit Test sag klasse. Jeg kalder det ConfigurableTestCase. Hver implementering af testkasser udvider denne klasse, se figur 2.

ConfigurableTestCase indeholder typisk de almindelige metoder og initialiseringskoden til testsagen. Jeg bruger en egenskabsfil til at gemme servernavnet, applikationskonteksten, forskellige loginnavne for hver rolle og nogle yderligere indstillinger.

De specifikke test-case implementeringer indeholder en testmetode pr. Test-case scenario (fra test-case specifikationsdokumentet). Hver metode logger typisk ind med en bestemt rolle og udfører derefter interaktionen med webapplikationen. De fleste testsager har ikke brug for en bestemt bruger for at udføre aktiviteterne; de kræver typisk en bruger i en bestemt rolle, som administrator, besøgende eller registreret bruger. Jeg opretter altid en LoginMode enum, som indeholder de tilgængelige roller. Jeg bruger Jakarta Commons ValuedEnum-pakke til at oprette enums til rollerne. Når en bestemt testmetode i en test-case-implementering logger ind, skal den specificere, hvilken login-rolle der kræves til det pågældende testscenarie. Muligheden for at logge ind med en bestemt bruger skal naturligvis også være mulig, for eksempel for at verificere brugen af ​​registreret bruger.

Efter hver anmodning og svarcyklus skal vi typisk kontrollere, om den returnerede side indeholder en fejl, og vi skal kontrollere vores påstande om, hvilket indhold svaret skal indeholde. Vi skal også være forsigtige her; vi skal kun kontrollere elementer, der ikke er variable og ikke for skrøbelige i applikationen. For eksempel, hvis vi hævder specifikke sidetitler, vil vores tests sandsynligvis ikke køre, hvis sproget kan vælges i applikationen, og vi vil kontrollere en anden sproginstallation. Tilsvarende er der ringe mening med at kontrollere et element på siden baseret på dets placering inden for et bordlayout; tabelbaserede design ændres ofte, så vi skal stræbe efter at identificere elementer baseret på deres id'er. Hvis nogle vigtige elementer på siden ikke har id'er eller navne, skal vi bare bede udviklerne om at tilføje dem i stedet for at prøve at omgå dem.

Påstande fra JUnit tilbyder en dårlig tilgang til kontrol af, om udseende og fornemmelse, layout og sidedesign overholder kravene. Det er muligt, givet uendelig lang tid til testudviklingen, men en god menneskelig tester kan vurdere disse ting mere effektivt. Så koncentrer dig om at kontrollere webapplikationens funktionalitet i stedet for at kontrollere alt muligt på siden.

Her er et opdateret testscenarie baseret på vores test-case-arkitektur. Klassen strækker sig ud ConfigurableTestCase, og loginoplysningerne håndteres i basisklassen:

 / ** * Kontrollerer, at indsendelse af loginformularen med navnet "master" resulterer * i en side, der indeholder teksten "Top Secret" ** / public void testGoodLogin () kaster Undtagelse {WebConversation samtale = ny WebConversation (); WebResponse svar = login (samtale, LoginMode.ADMIN_MODE); assertTrue ("Login accepteres ikke", response.getText (). indexOf ("Du klarede det!")! = -1); assertEquals ("Sidetitel", "Tophemmelighed", respons.getTitle ()); } 

Tips og tricks

De fleste scenarier kan håndteres ret let ved at indstille Webform parametre og derefter lede efter specifikke elementer med resultater i WebResponse sider, men der er altid nogle udfordrende testsager.