Automatiserad testning av GUI Mehmet Fatih Cicek (ada10mci@student.lu.se) Farhad Johari (ada10fjo@student.lu.se) 4/3 2014 Abstrakt Den här rapporten är en del av kursen Coaching av programvaruteam (EDA270) som ges på Lunds Tekniska Högskola. Rapporten innerhåller en genomgång av olika metoder som finns för att automatisera tester för ett användargränssnitt (GUI). Detta anser vi vara viktigt eftersom användargränssnittet är det första en användare kommer i kontakt med, och denna bör i högsta grad vara felfri. Manuell testning är tyvärr inte tillräcklig för att upptäcka alla (eventuella) defekter, och därför uppstår en önskan för automation. GUI testerna som utvecklas täcker funktionalitet i användargränssnitt som utvecklas av studenter på LTH. Studenterna ingår i team om 8 10 personer, och projektet de utvecklar är ett registrerings och sorteringsprogram till en Enduro tävling. Efter avslutat projekt följer en utvärdering om studenternas inställning till automatiserad GUI testning och huruvida detta är anpassat till projektet de utvecklar. Noterbart är att arbetsmetoden för projektet är extreme Programming (xp). 1
Innehållsförteckning Abstrakt Innehållsförteckning 1. Introduktion 2. Metoder 2.1 Manuell testning 2.2 Monkey testing 2.3 Capture and Replay 2.4 Event driven testing 3. Krav 4. Verktyg 4.1 jfcunit 4.2 FEST 4.3 Abbot 5. Analys 5.1 Praktiskt tillämpning 5.2 Utvärdering 6. Referenser 7. Appendix 2
1. Introduktion Projektet som utvecklarna tar fram programmeras under agila arbetsförhållanden, där test driven utveckling och automatiserade enhetstester har en stor och viktig roll. Detta innebär att all kod som utvecklas måste testas på förhand. Tyvärr inkluderas koden för användargränssnitten inte bland dessa enhetstester, och det kan ses som ett problem. Om man inte automatiserar GUI tester ökar risken för oupptäckta defekter, vilket man vill undvika i största möjliga grad. Detta beror på att man kan förbise fel, av misstag, när utfallet av tester verifieras manuellt [1]. Det är trotsallt via ett användargränssnitt en potentiell kund interagerar med programmet, och dennas intryck skall inte missdrabbas av ett oupptäckt problem. Dessutom kan man dra ner på tiden det tar för att testa ett användargränssnitt drastiskt om man övergår från manuell till automatiskt GUI testning. Det rapporteras att 52 timmars manuell testning av ett användargränssnitt endast tog tre timmar efter automation för ett projekt [2]. Detta är förstås proportionellt med användargränssnittets storlek, men ger en bra bild på hur effektiv automatiserad GUI testning kan vara. Dessutom rapporterar ADP (Automated Data Processing) att man har lyckats spara upp till 60% av tiden det tog för manuell testning med automatiska GUI tester [2]. Studier visar också att antalet buggar som påträffas med verktyg som stödjer automatiska GUI tester ökar mellan 10% till 50% [2]. Dessa exempel visar att automation kan bli en stor fördel i projekt där användargränssnittet är central och omfattande. Vårt mål är att introducera minst ett verktyg till teamet som kan användas för att producera automatiska GUI tester. Utveckling av dessa tester kommer framförallt att ske i samband med hemuppgifter som vi delar ut. Teamet kommer att komma i kontakt med verktyg halvvägs igenom projektet, och hur många verktyg som introduceras beror mycket på hur arbetet ligger till. Vi kommer inte stressa studenterna till att testa så många verktyg som möjligt, utan vårt mål är att de skall få en känsla av effekterna och hur viktigt det kan vara med GUI tester i ett riktigt projekt. Vi föredrar hellre utförliga GUI tester med ett verktyg än halvgjorda GUI tester med flera verktyg. Rapporten tar upp olika metoder för GUI tesning som finns beskrivet i sektion 2. Sektion 3 går igenom kraven vi ställer på verktygen som kan användas i projektet. Sektion 4 beskriver möjliga verktyg som kan användas i projektet, med för och nackdelar, och detta efterföljs av en analysdel där vi reflekterar över det som gick bra och mindre bra. Vi bifogar även de implementerade testfallen för användargränssnittet i ett appendix som finns längst bak i rapporten. 3
2. Metoder Det finns olika metoder för att testa ett användargränssnitt och alla metoder har både sina fördelar och nackdelar. Denna här sektionen går igenom några olika metoder, och motiverar varför vi väljer att utnyttja en viss metod för projektet som utvecklas. 2.1 Manuell testning Manuell testning är den enklaste formen av GUI testning. Man testar att användargränssnittet beter sig som förväntad genom att utföra några tester som producerar ett resultat. Resultatet kan lätt verifieras med förväntningarna och eventuella felaktigheter kan antecknas. Det krävs endast att testaren har kunskap om hur programmet är tänkt att fungera, dvs kunskap på domän och applikationsnivå [3] och oftast finns en checklista till hands som avser vad som behöver testas. Den här formen av testning är lönsam om användargränssnittet är litet, för då tar det inte mycket tid att testa allt som behöver testas. Men sådan är inte alltid fallet. Studier visar att 45 60% av all kod utgörs av användargränssnittet, och därför lyfts vikten av automatiserade GUI tester fram [4]. I vår arbetsmiljö måste alla enhetstester köras varje gång ett par utför en commit, dvs lägger upp produktionsklar kod på vårt gemensamma repositorie. Dessa tester omfattar inte användargränssnitten, och därför måste den testas manuellt. Eftersom vi jobbar med continuous integration och snabba releases i baby steps kan detta bli jobbigt med tiden. Vår förhoppning är att introducera ett verktyg som kan förenkla detta arbete genom automatiserade GUI tester. Huruvida det blir lönsamt i vårt fall diskuteras vid slutet av projektet. 2.2 Monkey testing Monkey testning är en mycket särskild typ av testning. Idén bygger på att stresstesta ett användargränssnitt för att hitta defekter. Verktyg som baseras på Monkey testing känner generellt sätt inte till användargränssnittet, och genererar slumpmässiga händelser automatiskt på GUI:t för att få applikationen att krascha eller bete sig på ett udda/oförväntat sätt, och kan upptäcka konstiga defekter som man vanligtvis missar. Ett bra exempel på en bugg som har hittats med denna metod berör den tyska versionen av Windows kalkylatorn på Windows Vista. Om man till exempel försöker dividera 57 med 0 framgår det i kalkylatorn av sådan division inte är möjlig. Om man därefter kallar på den interna hjälpen i kalkylatorn visas 100 i displayen, vilket visar att applikationen beter sig på ett oförväntat sätt [5]. Sådana typer av defekter är omöjliga att hitta med andra former av testning, och är möjligt här eftersom många olika sekvenser av input genereras till användargränssnittet. Statistikmässigt anger även Microsoft att de vid flertalet tillfällen har funnit 10 20% av sina buggar genom utnyttjandet utav denna typen av testning [5]. Det finns två olika kategorier av monkeys som används, nämligen smart och dumb monkeys. Skillnaden 4
på dessa är att en smart monkey känner till hur användargränssnittet ser ut medan en dumb monkey inte gör det. Detta gör att smart monkeys kan generera specifika händelser anpassat till användargränssnittet (därav namnet smart ), medan dumb monkeys genererar slumpmässiga händelser. Principen bygger dock fortfarande på samma grundidé Denna typ av testning används ofta som komplement till andra testningsmetoder. Det är vanligt att en dumb monkey utnyttjas i flera dagar innan lansering av ett program, för att försäkra dess utvecklare om att programmet inte kraschar eller beter sig udda. Projektet som studenterna tar fram har en specifik story som handlar om stresstestning. Om den här storyn blir aktuell och vid mån av tid kan man introducera ett lämpligt verktyg, men i allt annat fall kommer Monkey testing att undvikas. Monkey testing känns inte särkilt anpassat till agila projekt, under tiden projektet tas fram. Studenterna kan inte stresstesta användargränssnitten efter varje update och commit för att se om de fortfarande fungerar, detta är inte hållbart och effektivt. Dessutom är det inte säkert att samma fel produceras om igen, vilket kan vara viktigt när man vill hitta källan till defekten. Vi anser att denna metod har större effekt och slagkraft då den används på ett mycket större användargränssnitt, vid slutskedet av ett projekt. Vi anser även att utnyttjandet av bara denna metod för att GUI testning är väldigt bristfällig, och istället bör utvecklarna kombinera denna metod med någon annan metod för att testa gränssnitt till fullo. 2.3 Capture and Replay Capture and Replay är en automatiserad form av GUI testning där testaren skapar testen på samma vis som vid manuell testning, med undantaget att testen inte behöver utföras manuellt vid varje regressionstest [6]. Detta är möjligt eftersom testverktyget som används vid sådana test spelar in användarens interaktioner, i form av mus och tangntbordstryckningar/rörelser, med användargränssnittet och sparar detta som ett script [7, 8, 9]. När man vill testa GUI:t vid ett senare tillfälle kan verktyget återuppspela inspelningen, det vill säga scriptet, och verifiera resultatet med det som förväntas. Scriptet som spelas in har annorlunda språk och syntax, beroende på vilket testverktyg som används. Även om denna metod har en rätt så självklar tillvägagångssätt finns det två skilda varianter av Capture & Replay [7]. Dessa är nästintill identiska, med undantag från hur de spelar in GUI interaktionerna, vilket resulterar i att varje variant har sina egna fördelar och brister. En variant består av att GUI:t spelas in genom att man samlar in Bitmap värden för den grafiska representationen av GUI:t [7, 9]. Denna variant av Capture & Replay begränsar testprocessens stabilitet eftersom minsta ändring i användargränssnittets utseende resulterar i att testen fallerar. Med ändring i utseende avses att designen omformas på så sätt att GUI komponenter ändrar färg, storlek eller position. Alternativet till att spela in Bitmap värden är att spara undan referenser till GUI komponeneterna, på så sätt innebär inspelningen att man håller reda på komponenternas aktuella egenskaper/tillstånd [9]. Detta alternativ har inte den tidigare begränsningen för designändringar eftersom komponenterna inte längre detekteras visuellt utan testverktyget känner igen dessa antingen via deras typer och/eller namn. Detta ställer förstås ett krav på utvecklaren, nämligen att ge lämpliga namn till komponenterna. Ytterligare begränsas testdrivna utveckligsprocesser eftersom man redan i testfallen måste bestämma vilken typ av 5
komponenter som ska testas. Denna begränsning har däremot minskats i vissa testverktyg genom att man har skapat en egen komponent hierarki som möjliggör för verktyget att tolka flera komponenttyper som en typ. Vi kommer inte att introducera verktyg som baseras på Capture & Replay, av den enkla anledningen att designförändringar, som är vanligt återkommande, inte skall få GUI testerna att fallera. Eftersom en kund ofta ändrar sig känner vi inte att denna metod är särskilt anpassat till vårt projekt, och studenterna skall inte behöva göra om GUI tester efter hand som användargränssnitten ändras. Detta lär återspegla studenternas syn på GUI testning på ett negativt sätt, vilket vi vill undvika. Dessutom är studenterna andraårselever som endast har programmerat i Java och Eclipse. Vi vill hålla oss till detta, och inte introducera script med annorlunda syntax. 2.4 Event-driven testing Användargränssnitt drivs av händelser, vi förväntar oss en viss funktionalitet när man trycker på en knapp eller meny med uppdatering av respektive komponent. Av denna skäl bör GUI testerna generera likadana händelser för att simulera en verklig upplevelse. Resultatet av händelserna kan sedan jämföras med förväntningarna man har på dem. Event driven testning är just den här typen av testning, där man skickar händelser till en viss komponent på användargränssnittet och jämför resultatet. För att detta skall vara möjligt måste man kunna identifiera komponenterna i ett användargränssnitt så att rätt händelse kan genereras för rätt komponent [10]. För detta ändåmål finns det flera verktyg tillgodo, mer än detta i kommande sektion. Det viktigaste är att man namnger sina komponenter så att dessa kan kommas åt vid testerna. Därav måste komponenterna även ha unika namn, för att skilja dem åt. Enhetstester testar enskilda klasser och dess funktioner, men faktum råder att grafiska komponenter består av fler än en klass, och det blir betydligt svårare att testa dem på enhetsnivå [10]. Därför lämpar sig GUI tester på en funktionell nivå där man kan verifiera att systemet uppför sig som förväntad från en användares synvinkel [10]. Det som är positivt med event driven testning är att testerna är oberende användargränssnittets utseeende, till skillnad från Capture and Replay s vanligaste alternativ. Om en knapp flyttas ett par pixel till höger fallerar inte testerna på grund av det. Dessutom brukar dessa tester skrivas som traditionella enhetstester, vilket är positiv då de flesta programmerare har erfarenhet kring det. Vi anser denna metod vara mest lämpligast för vårt projekt på grund av naturen i ett användargränssnitt. En användare genererar händelser på ett användargränssnitt i form av knapp och tangentbordstryckningar, och därför känns det lämpligast om vi kan återskapa sådana fall i våra tester, för att göra dem så verklighetsbaserade som möjligt. 6
3. Krav Vi ställer en del krav på de verktyg som kan tänkas användas i vårt projekt för att generera GUI tester. Dessa krav är nödvändiga, och är utgångspunkten för våra val av verktyg. Verktygen som kan tänkas användas måste vara gratis för att kunna appliceras i vårt projekt. Vi har ingen budget som många andra företag för det här syftet. Det måste gå att integrera verktyget i Eclipse. I datorerna som projektet utvecklas på används Eclipse som IDE och därav följer det här kravet. Små förändringar på projektets användargränssnitt inte skall få testen att fallera. Detta eftersom projektet är i ständig förändring, och användargränssnitten ändras med tiden. Detta bör inte påverka testerna som har skrivits, och få dem att fallera. Designförändringar får alltså inte påverka testerna. Verktygen måste stödja Java eftersom studenterna programmerar i Java. Användargränssnitten består av Swing/AWT komponenter som är Java bibliotek. Verktygen bör utnyttja sig av JUnit Framework. Det här kravet följer eftersom många enhetstester utvecklas med JUnit under projektets gång, och det är smidigt om studenterna kan utveckla GUI testerna på liknande sätt. Det måste finnas stöd för TDD. Detta är till exempel möjligt om komponenterna går att identifiera. Annars är det omöjligt att generera händelser på diverse komponenter, och därmed omöjligt att skriva testfall som kan simulera verkliga händelser. Verktygen skall vara lätta att lära sig för att minimera den tid det tar att skriva testmetoder. Denna egenskap är väldigt viktigt för detta projekt då utvecklarna har en begränsad tid till sitt förfogande och därav vill vi spendera så lite tid som möjligt på inlärningsfasen. Verktyg blir mycket mer lättlärda genom att exempelvis erbjuda en bra och komplett dokumentation. Enkel installation är även plus i kanten. 7
4. Verktyg Vi har plockat fram några kandidater som kan tänkas användas i vårt projekt. För varje verktyg följer en kort beskrivning om hur de fungerar och vi lyfter även upp några för och nackdelar. Eftersom vi har brist på tid och studenterna också har annat som de måste lägga fokus på kan vi tyvärr inte applicera alla verktygen på vårt projekt. Vår önskan hade varit att testa de populäraste verktygen för att kunna göra en rättvis bedömning om vilket verktyg som passar vårt projekt bäst, men vi vill att studenterna först och främst koncentrerar sig på deras huvudsakliga fokus. 4.1 jfcunit jfcunit är ett verktyg som utvidgar JUnit och tillåter utvecklare att skriva testfall för Java Swing baserade applikationer [11]. Verktyget kan installeras som en gratis plug in till Eclipse eller via två jar filer som laddas in till projektets buildpath. Dessa är jfcunit.jar och jakarta regexp 1.2.jar. Eftersom verktyget baseras på JUnit är det viktigt att man använder JUnit 3.7 eller en högre version som är kompatibel med Eclipse. Efter det är det fritt fram att börja använda verktyget. Den senaste versionen av jfcunit är 2.08 och gavs ut 2004. Verktyget har tyvärr inte uppdaterats sen dess, och det kanske förklarar varför det är svårt att hitta bra exempel och tutorials på nätet. jfcunit har stöd för att lokalisera GUI komponenter (med förutsättning att alla har unika namn) och kan därefter generera diverse händelser på dessa. Detta kan vara allt från att klicka på en knapp till att mata in text i textfält. Detta är möjligt med verktygets specifika klasser NamedComponentFinder och TestHelper. Nedan följer en figur som sammanfattar hur detta går till. Användergränssnittet som skall testas har en knapp regbtn som NamedComponentFinder lokaliserar. Därefter genererar man en knapptryckning genom att avfyra en händelse med metoden enterclickandleave. För vidare exempel, se [12]. jfcunit har även stöd för Capture and Replay [11] och skripten sparas i XML format. Verktyget har Java dokumentation på klasser och metoder på den officiella hemsidan som utvecklare kan behöva utnyttja, samt några exempel på hur verktyget kan användas. Den relativt dåliga dokumentationen och bristande exempel täcks upp av verktygets enkla sätt att skriva testfall. Man utnyttjar notationer från JUnit som Test, setup, teardown osv. vilket vi anser vara positivt. Verktyget är helt klart en kanditat för projektet, trots bristfällig dokumentation. 8
4.2 FEST FEST är en samling moduler som står för Fixtures for Easy Software Testning och kan användas med både JUnit och TestNG [13, 14]. Modulen som användas i samband med Java Swing applikationer heter FEST Swing och kan laddas ner från projektets hemsida [15]. Till skillnad från jfcunit är FEST Swing bättre dokumenterat och hyfsat enkel att lära sig. FEST Swing kräver att man använder Java SE 5.0 eller senare [13] och uppdaterades senast 2010. Modulen består av flera jar filer, och vilka som bör användas i ett projekt förklaras här. Modulen utnyttjar AWT Robot för att generera användarinput [16]. AWT Robot kan generera olika typer av händelser, precis som en vanlig användare, och gör att testfallen blir så verklighetstrogna som möjligt. Med andra ord är FEST Swing ett verktyg som stödjer typen Event driven testning, precis som jfcunit. FEST Swing kan lokalisera komponenter via namn, typ eller annan kriterium. Därav är det viktigt att man namnger sina komponenter i användargränssnittet med unika namn så att dessa går att hitta. Under testen kan man se användargränssnittet simuleras viseullt vilket är positivt då man kan verifiera att testerna utför det som man tänkt sig. Nackdelen är att man inte kan använda datorn samtidigt som testerna körs. Detta är i och för sig inte en stor begränsning för FEST eller GUI testning generellt sätt, eftersom man vanligtvis inte använder datorn till annat under tiden testerna exekveras. Det finns olika alternativ att välja bland när man skriver testfall med FEST Swing. Man kan antigen jobba direkt med FEST Robot, eller med något som kallas för Fixtures. Fixtures förser specifika metoder för att simulera händelser på GUI komponenter och har stöd för att verifiera dessas tillstånd [17]. Det finns en Fixture per varje Swing komponent och har samma namn som komponenten med tillägget Fixture. Kod som skrivs med Fixtures blir självförklarande och detta är klart positivt. FEST känns på förhand som ett bra val av verktyg. 4.3 Abbot Abbot, som står för A better bot, är ett framework för att skapa automatiska tester till Javas GUI komponenter. Det är ett opensource verktyg, precis som de tidigare verktygen, och finns att ladda ner gratis. Den senaste versionen av verktyget (release 1.2) släpptes 2011. Verktygen kommer tillsammans med en editor Costello som har stöd för Capture and Replay [18]. Det som är intressant för vår del är att kunna skriva testerna genom programmering, precis som vanliga enhetstester. Detta stödjer Abbot genom att utnyttja JUnit [19]. Abbot är hyfsat väldokumenterat. ComponentFinder är klassen som utvecklare använder för att lokalisera olika GUI komponenter, och som det märks har alla verktyg vi har beskrivit tagit samma tillvägagångssätt för att lösa problemet med identifiering av olika komponenter. För nybörjare finns det Quick Start, User Guide, Examples och Tutorials sektionerna till hands på verktygets officiella hemsida. Dessutom kan man få en överblick över hur strukturen är uppbyggt i Overview. Eftersom testerna kan skrivas med 9
JUnit kan man använda jämförelsemetoderna som JUnit stödjer (assertequals, assertfalse etc) och detta kan komma väl till hand för att verifiera ett tillstånd. Precis som övriga verktyg installeras Abbot genom att lägga till en jar fil i det aktuella projektets buildpath. 5. Analys Analysdelen är uppdelad i två delar. Först beskriver vi hur vi gick tillväga för att introducera verktyg, och hur GUI testerna implementerades. Sedan tar vi upp feedback från studenterna och avgör huruvida GUI tester är anpassat för PVG projektet. 5.1 Praktiskt tillämpning Verktyg för GUI testning som stödjer den event drivna metoden introducerades för första gången som hemuppgift inför iteration 3 för en student. Vi var tyvärr inte precisa med vilka verkyg som skulle undersökas, och detta bidrog till att studenten mer eller mindre misslyckades med att producera färdiga tester för användargränssnitten. Under planeringsmötet bestämdes att FEST och Abbot skulle undersökas, men gick sedan över till jfcunit och UISpec4J. Oklarheten berodde lite på oss, eftersom vi inte riktigt visste vilket verktyg som borde introduceras. UISpec4J saknar grafisk representation, så man kan visuellt inte se vad som testas. Studenten tog fram ett litet kodskelett med ovanstående verktyg, men det var inget som vi kunde utnyttja direkt i vårt projekt. För att undvika samma problem förberedde vi nästa student som fick som hemuppgift att undersöka ett nytt verktyg (jfcunit). Förberedelsen inkluderade manual med exempel på färdiga tester. Dessutom skapade vi ett riktigt testfall för registreringsgränssnittet som studenten kunde utgå från. Dessa förberedelser fick en positiv inverkan, och studenten lyckades skapa flera testfall anpassade till vårt projekt. Dessutom prioriterade kunden in en egen story om GUI testning under iteration 4. Detta gjorde att studenten fick ytterligare tillfälle att fortsätta skriva GUI testerna. Anledningen till att vi introducerade jfcunit var att verkyget är enkelt att använda, och man förstår sig på exemplen som finns utan ansträngning, trots dålig dokumentation. Den färdiga manualen för jfcunit, som har sammanställts av två tidigare coacher, var ännu en anledning till vårt val av verkyg. Efter det misslyckade fallet ville vi introducera ett verktyg med snabb inlärning, som snabbt kan ge resultat, och med förutsättningarna som fanns var jfcunit ett givet val. Under iteration 4 noterade vi noterade hur kodtäckningen ändrades när alla GUI tester föll på plats. Nedan följder två bilder från iteration 4 (kl 10.30) som visar andelen kod som testas före och efter GUI testen. Notera att sorteringsgränssnittet inte var testat när dessa skärmdumpar togs, men även det testades senare under iterationen (bland annat under de 2h som kunden prioriterade). 10
Code coverage innan GUI testerna har körts. Code coverage efter GUI testerna har körts. Vi får betydligt mer kod testad i projektet, och det kan bidra till enklare identifiering av eventuella fel i systemet. GUI testen visades upp för kunden, och han var nöjd och imponerad över resultatet vilket vi finner positivt. Vi tror att den visuella feedbacken man får med jfcunit kan ha en stor påverkan i detta. Vi upplevde något som kan ses som ett problem med jfcunit, nämligen att GUI testerna inte förblev helt automatiska till en början. Detta beror på att både registrerings och sorteringsgränssnittet anropar på externa fönster (för att öppna/spara filer), som man inte lyckades hantera. Detta lyckades man lösa för registreringsprogrammet genom att införa en flagga som avgör om sökvägen till en viss fil, i samband med öppna/spara, skall vara dynamisk eller statisk. Man körde på samma koncept med statiska sökväg för vissa filer till sorteringsprogrammet, men att få allt automatiskt var lite omständigare då sorteringsgränssnittet anropade på fler externa fönster. Detta ser vi som en stor begränsning, och sådana justeringar skall inte behövas för att få testerna att köras utan interaktion med användaren. De flesta av ovanstående problem kunde ha undvikits med en konfigurationsfil (story 25), men kunden valde att inte prioritera det. Under femte planeringsmötet fick en student som hemuppgift att undersöka FEST Swing, och skriva testfall för vårt projekt med verktyget. Tanken var att kunna göra en jämförelse mellan två olika verktyg, och se ifall det ena är med lämpad än det andra för vårt projekt. Tyvärr drabbades studenten av ett slags virus, och kunde inte påbörja sin hemuppgift. Dessutom fick vi ingen betald tid under iteration 5 för GUI testning, så därför fick vi inget gjort den här veckan. Inför sista iterationen hade vi som mål att få story 9 (varvlopp) fullbordat, då den hade stökat till det 11
ordentligt sedan iteration 2. Eftersom vi hade en release framför oss valde vi att inte introducera ett nytt verktyg. En av studenterna som hade hållt på med GUI testning gjorde klart sin hemuppgift relativt snabbt, och ville gärna få annat arbete som kunde gynna teamet. Detta såg vi som ett ypperligt tillfälle för att optimera GUI testerna för sorteringsprogrammet (som vid det här laget inte var helt automatiska). Studenten tog sig an uppgiften, och under iterationen sågs det till att alla GUI tester blev helt automatiska. Innan release 3 skickades iväg kollade vi på kodtäckningen och den visade sig ligga på 93%, vilket är väldigt högt. Main metoderna var också testade, vilket är lite onödigt, och om man exkluderade dem från testerna hamnade kodtäckningen på 91.9%. Tyvärr innehåll projektet en del tester som inte gick igenom på grund av story 9 som inte helt var på plats (tidsbegränsning saknades). Även om testerna fallerade täcktes vissa delar av koden, men teamet bestämde sig för att ignorera dessa tester (@Ignore i JUnit)och då sjönk kodtäckningen till 84.5% (vilket fortfarande är högt). Nedan kan man se två figurer som visar kodtäckningen på releasen som skickades iväg den sista iterationen. Som det syns höjdes kodtäckningen ganska mycket av våra GUI tester. Det fanns fortfarande rum för förbättringar, men på grund av tidsbrist hann vi inte implementera det. Code coverage innan GUI testerna har körts. Code coverage efter GUI testerna har körts. 5.2 Utvärdering Vi frågade teamet hur ofta de testade användargränssnitten (innan GUI testerna) när ny funktionalitet lades till i systemet, och vi möttes av det förväntade svaret; i princip aldrig. Alla var eniga om att det egentligen borde testas då enhetstesterna inte täckte användargränssnitten, men ingen orkade riktigt göra det. Till deras försvar hörde argument som Det tar för lång tid, Det funkar säkert osv. Vi ställde samma fråga efter den sista releasen, och märkte att många fler testade användargränssnitten. Till en 12
början var testerna semi automatiska, och det bidrog till att man inte körde dem efter ett tag, men så fort allt sköttes automatiskt märktes det att användningen av GUI testerna ökade. Detta bekräftade även teamet, och konstaterade att man sparar en hel del tid med automatiska tester. Vårt projekt bestod av två användargränssnitt. Sorteringsprogrammet var egentligen tänkt att vara terminalbaserad, men teamet övergick till ett grafisk användargränssnitt efter diskussion med kunden. Detta gav oss ännu en anledning till att introducera GUI verktyg då vi fick mer GUI baserad kod än det tänkta fallet. Eftersom vi hade mer att testa kunde studenterna ut mer av GUI testerna, och verkligen se effekterna. Alla fick tyvärr inte chansen till att implementera dessa tester, då vi inte hade tillräckligt med tid. Vi lyckades inte heller introducera ett annat verktyg (förutom jfcunit) av samma anledning, men målet var att de skulle få en känsla av effekterna och hur viktigt det kan vara med GUI tester i ett riktigt projekt, och av feedbacken vi fick kan man konstatera att vi lyckades med detta. Först och främst kommenterade studenterna på hur fräck testerna såg ut. De uppskattade att få jobba utöver det vanliga, där man kan få visuell feedback och se om man har gjort rätt eller fel. Att testerna skrevs som vanliga JUnit tester förenklade testskrivandet en hel del, enligt de involverade studenterna. Paketindelningen som användes i projektet gjorde så att användargränssnitten och logiken bakom gick att testa separat (MVC modellen), vilket kan också ses som en förenklande faktor. Tyvärr var datorerna väldigt söliga, och det påverkade hur ofta teamet var villiga till att köra testerna efter ett tag. Vi frågade specifikt vad de tyckte om jfcunit, och feedbacken vi fick av de två studerande som hade jobbat med verktyget var att det var enkelt att skriva testfall, inte alls svårt att sätta sig in i verktyget (trots dålig dokumentation), men ansågs även vara bristfällig då det var väldigt svårt att hantera externa fönster som inte tillhörde användargränssnitten. Detta gick att lösa med flaggor i programmet, men ansågs inte vara en särskild snygg lösning. Överlag var studenterna nöjda med GUI testerna, och väldigt positiva till att dessa tester ökade kodtäckningen i projektet. De rekommenderade övriga team att implementera automatiska GUI tester efter deras positiva erfarenheter. Från feedbacken vi fick konstaterades att studenterna nöjda över resultatet, och det gör att automatiska GUI tester känns applicerbart för PVG projektet. Det beror också på att teamet lyckades göra de semi automatiska testerna helt automatiska. Vi visade upp de automatiska testerna för kunden, och han var helt klart imponerad och såg hela vår investering lönsamt. För honom var stabilitet viktigt, och han ansåg våra tester uppfylla hans stabilitetskrav. Därmed fick vi fram något som både studenterna och kunden var nöjd över, vilket gör oss som coacher väldigt glada. Vi kan rekommendera GUI testning och verktyget jfcunit till övriga team, men vill påpeka att man inte bör göra det på kostnad av annan fokus som studenterna har. Det bör istället göras under hemuppgifter, och där kunden faktiskt betalar efter en överenskommelse. 13
6. Referenser [1] M. Finsterwalder, Automating Acceptance Tests for GUI Applications in an Extreme Programming Environment, University of Hamburg, Germany. In Proceedings of the 2nd International Conference on Extreme Programming and Flexible Processes in Software Engineering. [2] Kanglin Li,Mengqi Wu. Effective GUI Testing Automation: Developing an Automated GUI Testing Tool, 2004. ISBN: 978 0 7821 5067 4. [3] Alessandro Marchetto & Fondazione Bruno Kessler IRST Seminar Gui based Testning http://selab.fbk.eu/swat/slide/2.5_gui.ppt%e2%80%8e [4] A. M. Memon, A comprehensive framework for testing graphical user interfaces. PhD thesis, University of Pittsburgh, 2001. [5] B. Hofer and B. Peischl and F. Wotawa, GUI Savvy End to End Testing with Smart Monkeys, Technische Universitat Graz, Austria. IEEE Digital Object Identifier 10.1109/IWAST.2009.5069051. [6] Atif M. Memon and Bao N. Nguyen, Advances in Automated Model Based System Testing of Software Applications with a GUI Front End http://www.sciencedirect.com/science/article/pii/s0065245810800038 [7] Borjesson, E. and Feldt, R., Automated System Testing Using Visual GUI Testing Tools: A Comparative Study in Industry. IEEE Digital Object Identifier 10.1109/ICST.2012.115. [8] Ruiz, A and Price, Y.W., Test Driven GUI Development with TestNG and Abbot. IEEE Digital Object Identifier 10.1109/MS.2007.92. [9] Emil Alégroth, Robert Feldt and Lisa Ryrholm, Visual GUI testing in practice: challenges, problems and limitations. Digital Objevt Identifier 10.1007/s10664 013 9293 5. [10] Alex Ruiz and Yvonne Wang Price. Gui testing made easy. In Proceedings of the Testing: Academic & Industrial Conference. Digital Object Identifier 10.1109/TAIC.PART.2008.11. [11] jfcunit User Documentation. http://jfcunit.sourceforge.net/ (Senast uppdaterad: 31/12/2004, Senast visad: 3/3/2014) 14
[12] JFCUnit Overview. 2006. http://www.csie.ntut.edu.tw/labsdtl/95 summer/0906 1.pdf [13] FEST Swing Module. http://fest.codehaus.org/swing+module (Senast uppdaterad: Okänt, Senast visad 3/3/2014) [14] FEST Swing Module. http://docs.codehaus.org/display/fest/swing+module (Senast uppdaterad: 28/8/2010, Senast visad: 3/3/2010) [15] Fixtures for Easy Software Testing. http://code.google.com/p/fest (Senast uppdaterad: 28/2/2011, Senast visad: 3/3/2014) [16] FEST Swing Module. http://fest.codehaus.org/input+simulation (Senast uppdaterad: Okänt, Senast visad 3/3/2014) [17] FEST Swing Module. http://fest.codehaus.org/getting+started (Senast uppdaterad: Okänt, Senast visad 3/3/2014) [18] Abbot Java GUI Test Framework. http://abbot.sourceforge.net/doc/overview.shtml (Senast uppdaterad: 30/12/2011, Senast visad 3/3/2014) [19] Abbot API specification. http://abbot.sourceforge.net/doc/api/overview summary.html#overview_description (Senast uppdaterad: 2008, Senast visad 3/3/2014) 15
7. Appendix Här bifogas användargränssnitten som utvecklarna har tagit fram samt de GUI test som har skrivits och går igenom. Tanken är att vem som helst skall kunna kopiera in dessa till Eclipse (eller annan IDE) och köra dem lokalt. RegistrationGUI (Detta är användargränssnittet som sköter registrering av förare). Main metoden som startar programmet finns i en seperat klass. packager.gui; importjava.awt.borderlayout; importjava.awt.color; importjava.awt.font; importjava.awt.gridlayout; importjavax.swing.boxlayout; importjavax.swing.jbutton; importjavax.swing.jframe; importjavax.swing.jlabel; importjavax.swing.jpanel; importjavax.swing.jscrollpane; importjavax.swing.jtextarea; importjavax.swing.jtextfield; importjavax.swing.swingconstants; importr.controller.enteractionlistener; importr.controller.regallbuttonlistener; importr.controller.regbuttonlistener; importr.model.timeregisterhandler; 16
importcommons.savefilechooser; publicclassregistrationguiextendsjframe{ privatestaticfinallongserialversionuid=1l; privatejtextfieldregisterfield; privatejtextarearecentreg; /** * ContentoftheregistrationGui. * *@paramistest * theguiisusedinatestcase. */ publicregistrationgui(booleanistest){ Stringpath=""; if(istest){ path=system.getproperty("user.home") +"/registerresults.txt"; else{ path=newsavefilechooser().getpath(""); if(path==null){ System.exit(0); setbounds(100, 100, 520, 300); setdefaultcloseoperation(jframe.exit_on_close); JPanelregisterPanel=newJPanel(); registerpanel.setbackground(color.white); getcontentpane().add(registerpanel, BorderLayout.NORTH); registerpanel.setlayout(newboxlayout(registerpanel, BoxLayout.X_AXIS)); JPanelpanel=newJPanel(); registerpanel.add(panel); panel.setlayout(newborderlayout(0, 0)); JLabellblStartnumber=newJLabel("Startnummer:"); lblstartnumber.sethorizontalalignment(swingconstants.center); panel.add(lblstartnumber, BorderLayout.NORTH); lblstartnumber.setbackground(color.white); lblstartnumber.setfont(newfont("dialog", Font.BOLD, 20)); registerfield=newjtextfield(); registerfield.setname("registerfield"); panel.add(registerfield); registerfield.sethorizontalalignment(swingconstants.center); registerfield.setfont(newfont("dialog", Font.PLAIN, 42)); registerfield.setcolumns(10); JPanelpanel_1=newJPanel(); registerpanel.add(panel_1); 17
panel_1.setlayout(newgridlayout(0, 1, 0, 0)); TimeRegisterHandlertrh=newTimeRegisterHandler(path); JPanelentriesPanel=newJPanel(); getcontentpane().add(entriespanel, BorderLayout.CENTER); entriespanel.setlayout(newgridlayout(0, 1, 0, 0)); recentreg=newjtextarea(); recentreg.setname("registerlist"); recentreg.seteditable(false); JScrollPanesp=newJScrollPane(recentReg); entriespanel.add(sp); JButtonregisterButton=newJButton("Registrera"); registerbutton.setname("registerbutton"); panel_1.add(registerbutton); registerbutton.setfont(newfont("dialog", Font.BOLD, 28)); registerfield.addactionlistener(newenteractionlistener(trh, this)); JButtonbtnMass=newJButton("Mass-start"); btnmass.setname("btnmass"); btnmass.setfont(newfont("dialog", Font.BOLD, 28)); btnmass.addactionlistener(newregallbuttonlistener(trh, this)); panel_1.add(btnmass); registerbutton.addactionlistener(newregbuttonlistener(trh, this)); setvisible(true); /** * AppendsaStringtothemessagefieldintheGUI. * *@paramtext * toappend. */ publicvoidappend(stringtext){ recentreg.append(text); registerfield.settext(""); registerfield.requestfocus(); /** * Methodtogetthetextenteredintotheregistrationfield. * *@returnthetextintheregistrationfield. */ publicstringgetregfieldtext(){ 18
try{ returnregisterfield.gettext(); catch(exceptione){ return""; SortingGUI (Detta är användargränssnittet som sköter sorteringen). Main metoden som startar programmet finns i en seperat klass. packages.gui; importjavax.swing.jbutton; importjavax.swing.jframe; importjavax.swing.jscrollpane; importjavax.swing.jtextarea; imports.controller.endbuttonlistener; imports.controller.namebuttonlistener; imports.controller.sortbuttonlistener; 19
imports.controller.startbuttonlistener; imports.model.laptimehandler; imports.model.racerhandler; imports.model.time; publicclasssortingguiextendsjframe{ privatestaticfinallongserialversionuid=1l; privatejtextareaarea; publicsortinggui(stringstring){ super("sortering"); RacerHandlerrh; if(string.equals("test")){ StringtimeLimit="13.00.00"; intmaxlapswrite=3; rh=newlaptimehandler(timelimit, maxlapswrite, new Time("00.15.00")); else{ RaceTypeDialogrtd=newRaceTypeDialog(); rh=rtd.getracetype(); getcontentpane().setlayout(null); setbounds(100, 100, 521, 540); setdefaultcloseoperation(jframe.exit_on_close); JButtonbtnLoadStartFile=newJButton("LaddaStartfil"); btnloadstartfile.addactionlistener(newstartbuttonlistener(rh, this,string)); btnloadstartfile.setbounds(12, 12, 490, 74); btnloadstartfile.setname("loadstartbtn"); getcontentpane().add(btnloadstartfile); JButtonbtnLoadEndFile=newJButton("LaddaMÃ lfil"); btnloadendfile.addactionlistener(newendbuttonlistener(rh, this,string)); btnloadendfile.setbounds(12, 98, 490, 74); btnloadendfile.setname("loadendbtn"); getcontentpane().add(btnloadendfile); JButtonbtnLoadNameFile=newJButton("LaddaNamnfil"); btnloadnamefile.addactionlistener(newnamebuttonlistener(rh,this,string)); btnloadnamefile.setbounds(12, 184, 490, 74); btnloadnamefile.setname("loadnamebtn"); getcontentpane().add(btnloadnamefile); JButtonbtnSort=newJButton("Sortera"); btnsort.addactionlistener(newsortbuttonlistener(rh, this,string)); btnsort.setbounds(12, 270, 490, 74); btnsort.setname("sortbtn"); getcontentpane().add(btnsort); 20
JScrollPanescrollPane=newJScrollPane(); scrollpane.setbounds(12, 360, 490, 142); getcontentpane().add(scrollpane); area=newjtextarea(); area.setname("textfield"); area.seteditable(false); scrollpane.setviewportview(area); setvisible(true); publicvoidappend(stringtext){ area.append(text+"\n"); jfcunit packagetestgui; importjava.awt.event.keyevent; importjavax.swing.jbutton; importjavax.swing.jcomponent; importjavax.swing.jtextarea; importjavax.swing.jtextfield; importjunit.extensions.jfcunit.jfctestcase; importjunit.extensions.jfcunit.jfctesthelper; importjunit.extensions.jfcunit.eventdata.keyeventdata; importjunit.extensions.jfcunit.eventdata.mouseeventdata; importjunit.extensions.jfcunit.finder.namedcomponentfinder; importorg.junit.test; importr.gui.registrationgui; importr.model.currenttime; publicclasstestregisterguiextendsjfctestcase{ privateregistrationguigui=null; privatecurrenttimecurrenttime; protectedvoidsetup() throwsexception{ currenttime=newcurrenttime(); super.setup(); sethelper(newjfctesthelper()); gui=newregistrationgui(true); 21
gui.setvisible(true); /** * Forthesetestthetesterneedtomanuallywriteafilenameintothe * savewindow */ @Test publicvoidtestregistergui(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"registerField"); JTextFieldregisterField=(JTextField) finder.find(gui, 0); assertnotnull("textfielddoesnotexits", registerfield); assertequals("textfiledshouldbeempty","", registerfield.gettext()); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_5)); assertequals("textfieldshouldcontain5","5", registerfield.gettext()); finder.setname("registerlist"); JTextArearegisterArea=(JTextArea) finder.find(gui, 0); assertnotnull("jtextareadoesnotexits", registerarea); assertequals("jtextareashouldbeempty","", registerarea.gettext()); finder.setname("registerbutton"); JButtonregisterButton=(JButton) finder.find(gui, 0); assertnotnull("registerbuttondoesnotexits", registerbutton); assertequals("registerbuttonshouldsay'registrera'","registrera", registerbutton.gettext()); gethelper().enterclickandleave(newmouseeventdata(this, registerbutton)); StringcurrentTimeString=currentTime.toString(); assertequals("jtextareashouldbefilled", "5;" +currenttimestring.substring(0, 6), registerarea.gettext().substring(0, 9)); booleansecondsmatch=false; StringsecondsString=registerArea.getText().substring(9, 11); intseconds=integer.parseint(secondsstring); secondsmatch=((integer.parseint(currenttimestring.substring(6, 8)) == seconds) (Integer.parseInt(currentTimeString.substring(6, 8)) ==seconds+ 1)); asserttrue("secondsdonotmatch", secondsmatch); @Test 22
publicvoidtestregisterguiwronginput(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"registerField"); JTextFieldregisterField=(JTextField) finder.find(gui, 0); assertnotnull("textfielddoesnotexits", registerfield); assertequals("textfiledshouldbeempty","", registerfield.gettext()); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_SLASH)); assertequals("textfieldshouldcontain/","/", registerfield.gettext()); finder.setname("registerlist"); JTextArearegisterArea=(JTextArea) finder.find(gui, 0); assertnotnull("jtextareadoesnotexits", registerarea); assertequals("jtextareashouldbeempty","", registerarea.gettext()); finder.setname("registerbutton"); JButtonregisterButton=(JButton) finder.find(gui, 0); assertnotnull("registerbuttondoesnotexits", registerbutton); assertequals("registerbuttonshouldsay'registrera'","registrera", registerbutton.gettext()); gethelper().enterclickandleave(newmouseeventdata(this, registerbutton)); assertequals("jtextareashouldshowexception","felinmatning\n", registerarea.gettext()); @Test publicvoidtestregisterguimassstart(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"registerField"); JTextFieldregisterField=(JTextField) finder.find(gui, 0); assertnotnull("textfielddoesnotexits", registerfield); assertequals("textfiledshouldbeempty","", registerfield.gettext()); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_1)); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_MINUS)); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_3)); assertequals("textfieldshouldcontain1-3","1-3", registerfield.gettext()); finder.setname("registerlist"); JTextArearegisterArea=(JTextArea) finder.find(gui, 0); assertnotnull("jtextareadoesnotexits", registerarea); assertequals("jtextareashouldbeempty","", registerarea.gettext()); finder.setname("registerbutton"); JButtonregisterButton=(JButton) finder.find(gui, 0); assertnotnull("registerbuttondoesnotexits", registerbutton); assertequals("registerbuttonshouldsay'registrera'","registrera", 23
registerbutton)); gethelper() registerbutton.gettext());.enterclickandleave(newmouseeventdata(this, StringcurrentTimeString=currentTime.toString(); assertequals("jtextareashouldbefilled", "1-3;" +currenttimestring.substring(0, 6), registerarea.gettext().substring(0, 11)); booleansecondsmatch=false; StringsecondsString=registerArea.getText().substring(11, 13); intseconds=integer.parseint(secondsstring); secondsmatch=((integer.parseint(currenttimestring.substring(6, 8)) == seconds) (Integer.parseInt(currentTimeString.substring(6, 8)) ==seconds+ 1)); asserttrue("secondsdonotmatch", secondsmatch); @Test publicvoidtestregisterguiwrongordermassstart(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"registerField"); JTextFieldregisterField=(JTextField) finder.find(gui, 0); assertnotnull("textfielddoesnotexits", registerfield); assertequals("textfiledshouldbeempty","", registerfield.gettext()); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_3)); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_MINUS)); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_1)); assertequals("textfieldshouldcontain3-1","3-1", registerfield.gettext()); finder.setname("registerlist"); JTextArearegisterArea=(JTextArea) finder.find(gui, 0); assertnotnull("jtextareadoesnotexits", registerarea); assertequals("jtextareashouldbeempty","", registerarea.gettext()); finder.setname("registerbutton"); JButtonregisterButton=(JButton) finder.find(gui, 0); assertnotnull("registerbuttondoesnotexits", registerbutton); assertequals("registerbuttonshouldsay'registrera'","registrera", registerbutton.gettext()); gethelper().enterclickandleave(newmouseeventdata(this, 24
registerbutton)); assertequals("jtextareashouldbefilled", "Detförstataletmåstevaramindreändetandra\n", registerarea.gettext()); @Test publicvoidtestmassbutton(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"registerField"); JTextFieldregisterField=(JTextField) finder.find(gui, 0); assertnotnull("textfielddoesnotexits", registerfield); assertequals("textfiledshouldbeempty","", registerfield.gettext()); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_1)); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_MINUS)); gethelper().sendkeyaction( newkeyeventdata(this, registerfield, KeyEvent.VK_3)); finder.setname("btnmass"); JButtonbtnMass=(JButton) finder.find(gui, 0); gethelper().enterclickandleave(newmouseeventdata(this, btnmass)); finder.setname("registerlist"); JTextArearegisterArea=(JTextArea) finder.find(gui, 0); StringcurrentTimeString=currentTime.toString(); Stringactual=registerArea.getText().substring(0, 27); Stringtemp=currentTimeString.substring(0, 5); Stringexpected="Mass-startmedtiden:" +temp; assertequals("jtextareashouldbefilled", expected, actual); booleansecondsmatch=false; StringsecondsString=registerArea.getText().substring(28, 30); intseconds=integer.parseint(secondsstring); secondsmatch=((integer.parseint(currenttimestring.substring(6, 8)) == seconds) (Integer.parseInt(currentTimeString.substring(6, 8)) ==seconds+ 1)); asserttrue("secondsdonotmatch", secondsmatch); packagetestgui; importjavax.swing.jbutton; importjavax.swing.jcomponent; importjavax.swing.jtextarea; importjunit.extensions.jfcunit.jfctestcase; importjunit.extensions.jfcunit.jfctesthelper; importjunit.extensions.jfcunit.eventdata.mouseeventdata; importjunit.extensions.jfcunit.finder.namedcomponentfinder; 25
importorg.junit.test; imports.gui.sortinggui; publicclasstestsortingguiextendsjfctestcase{ privatesortingguigui=null; protectedvoidsetup() throwsexception{ super.setup(); // junit.extensions.jfcunit.windowmonitor.start(); sethelper(newjfctesthelper()); gui=newsortinggui("test"); gui.setvisible(true); /** * Thistestneedstoopenuser.dir+SaveFiles/iteration1/starttider.txt */ @Test publicvoidtestastartbtn(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"TextField"); JTextAreatextArea=(JTextArea) finder.find(gui, 0); finder.setname("loadstartbtn"); JButtonloadStartBtn=(JButton) finder.find(gui, 0); assertnotnull("startbtndoesnotexits", loadstartbtn); assertequals("startbtnshouldnotbeempty","laddastartfil", loadstartbtn.gettext()); gethelper().enterclickandleave(newmouseeventdata(this, loadstartbtn)); Stringpath=System.getProperty("user.dir") +"/SaveFiles/Iteration1/starttider.txt"; assertequals("textareashouldbenotempty", "Inläsningavstartfillyckades\n" +path+"\n", textarea.gettext()); @Test publicvoidtesttextarea(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"TextField"); JTextAreatextArea=(JTextArea) finder.find(gui, 0); assertnotnull("textareadoesnotexits", textarea); assertequals("textareashouldbeempty","", textarea.gettext()); @Test publicvoidtestendbtn(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"LoadEndBtn"); JButtonloadEndBtn=(JButton) finder.find(gui, 0); 26
assertnotnull("endbtndoesnotexits", loadendbtn); assertequals("endbtnshouldnotbeempty","laddamålfil", loadendbtn.gettext()); @Test publicvoidtestnamebtn(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"LoadNameBtn"); JButtonloadNameBtn=(JButton) finder.find(gui, 0); assertnotnull("loadnamebtndoesnotexits", loadnamebtn); assertequals("loadnamebtnshouldnotbeempty","laddanamnfil", loadnamebtn.gettext()); /** * Thistestneedsthatthesortingresultissavedasuser.dir+ * SaveFiles/iteration1/guiTest.txt */ @Test publicvoidtestsortbtn(){ NamedComponentFinderfinder=newNamedComponentFinder( JComponent.class,"TextField"); JTextAreatextArea=(JTextArea) finder.find(gui, 0); finder.setname("sortbtn"); JButtonsortBtn=(JButton) finder.find(gui, 0); assertnotnull("sortbtndoesnotexits", sortbtn); assertequals("sortbtnshouldnotbeempty","sortera", sortbtn.gettext()); gethelper().enterclickandleave(newmouseeventdata(this, sortbtn)); Stringpath=System.getProperty("user.dir") +"/SaveFiles/Iteration1/guiTest.txt"; assertequals("textareashouldbenotempty", "Genereringavresulatatlistalyckades.\n", textarea.gettext()); 27