Klassificering av testdata

Typewriter-2

Testdata är ett kärt ämne så jag tänkte vi kunde prata lite om olika typer av testdata och hur man kan klassificera och hantera dessa. Jag tänkte begränsa mig till persistent data.

Ett grundkriterie för robusta tester är att man har kontroll över systemets tillstånd. Ett och samma test kan ge olika resultat om systemets tillstånd skiljer sig åt mellan testkörningar. Än värre är om testerna har ett inbördes beroende. T.ex. om man byter exekveringsordning på två tester så får man ett annat resultat. Det kan t.ex. bero på att testerna delar data och det ena testet förändrar datan. Det är dock inte praktiskt möjligt att helt kontrollera systemets tillstånd – iallafall inte för systemtester – men man bör eftersträva att ha så stor kontroll som möjligt.

Vad är det som påverkar systemets tillstånd? All data som inte återställs eller rensas förändrar tillståndet hos systemet. Denna data kan komma i en uppsjö av olika varianter, ex ; cache, fil, kö, databas, klassvariabler etc. För att vara helt säker på att systemet är återställt måste man starta om det, samt rensa alla persistenta källor mellan varje test. Det kan man sällan göra av praktiska skäl – då skulle testerna ta för lång tid att exekvera. Dock brukar det finnas möjlighet att kontrollera den persistenta datan (icke-dynamiska data) – som jag här tänkte representera med en databas.

Den persistena datan kan klassificeras i tre huvudkategorier; ”färsk”, ”oföränderlig” och ”delad”.

”Färsk”, är data som endast existerar under livstiden av ett oberoende test. Det kan vara en förutsättning för testet eller data som skapas av testet. Efter testet så återställs systemet genom att rensa bort den nya datan. Ex:

Tänk er ett system med möjlighet att registrera unika kunder – inte helt ovanligt. Vi testar att registrera ”Nils” vilket genererar nya rader i diverse tabeller i databasen. För att kunna köra exakt samma test igen måste datan knuten till kunden ”Nils” tas bort – då systemet endast tillåter unika kunder. Det gör man förslagsvis i någon post-metod (tearDown) med hjälp av lämplig systemmetod (deleteCustomer). Om dylik systemmetod saknas (låg testabilitet) blir det knepigare. Ska man skriva specifik testkod för att ta bort denna data? Inte helt självklart. Då kan det vara bättre att undersöka möjligheterna till att göra en ”transaction rollback”.

”Oföränderlig”, är data som inte förändras under sin existens. Existensen definieras som livstiden av alla tester. Det kanske vanligaste exemplet på denna typ av data är konfigurering Då denna data inte förändras för någon av testerna blir den lätthanterlig. Förslagsvis laddar man denna data innan första testet körs. Det finns ingen anledning att rensa eller återställa denna data mellan testerna.

Slutligen kommer vi till den mest komplexa datatypen – ”delad”. Precis som ovan typ existerar denna data under livstiden av alla tester. Skillnaden ligger i att datan kan förändras. Ex:

Tänk er att vi förladdar databasen med ett förutbestämt set av kunder. Vi hämtar sedan en kund från detta set inför varje specifikt test och använder som testdata. Problem uppstår när ett test modifierar någon egenskap hos en specifik kund och ett annat test återanvänder just den kunden. Säg att vi har ett test som markerar en kund som obsolet. Om ett annat test återanvänder den kunden kommer det testet att fallera då kunden inte är brukbar.

Man bör eftersträva att endast använda ”färsk” och ”oföränderlig” data. I praktiken kan det dock vara svårt pga låg testabilitet etc. Då kan man försöka att bryta upp sin ”delade” data i ovan två typer. Det ger mer kontroll och bättre robusthet. Om vi använder ovan exempel så skulle vi åstadkomma detta genom att göra kundens egenskaper till ”färska”. Huruvida det är praktiskt lämpligt beror på hur systemet är implementerat.

 

 

Skapa persistent testdata

Typewriter-2

En fråga som ploppar upp med jämna mellanrum är hur man bäst skapar testdata för sina automatiska tester. Om vi begränsar diskussionen till persistent data så finns det två huvudfalanger; populera datan med egna testscript eller genom att använda systemets gränssnitt.

Den första varianten betyder i praktiken (oftast) att skapa testspecifika anrop till databasen för att populera datan. Att generera testdatan m.h.a. systemets gränssnitt betyder att testet ropar på någon inneboende metod i systemet och låter systemet skapa datan. Denna metod kan antingen vara publik eller intern.

Så vad är för/nackdelar med dessa två varianter?

Att skapa egna script för testdatagenerering innebär att dessa måste underhållas när systemet förändras. Det kan höja förvaltningskostnaden för testerna. Vidare ökar komplexiteten då kan det finnas fel i själva testscripten.

Å andra sidan, om man använder interna systemmetoder för att skapa datan finns risk att man skapar datan felaktigt – det är inte syftet med den interna metoden. Publika systemmetoder bör representera verkligheten, men om vi har defekter i systemet kan vi inte generara testdatan och därmed inte heller utföra våra tester. Vi kan hamna i en situation där alla tester går sönder pga att en systemfunktionalitet inte fungerar.

Vad bör vi överväga när vi väljer strategi? Låt oss titta på ytterligheter för när respektive metod fungerar bäst.

Ponera ett befintligt system med små förändringar där testerna exekveras sällan (några ggr per release). Om vi här nyttjar en strategi med testscript så bör underhållet av testscripten vara minimalt. När testerna väl exekveras så vill man inte riskera att en stor del av testerna fallrar pga att man inte kan sätta upp förutsättningarna. Då får man ett meningslöst resultat.

Motsatsen är ett system med stora förändringar. Om testerna exekveras ofta (flera ggr per dag) fångar man snabbt upp eventuella problem med att systemet inte kan generera testdatan. Här ter sig nyttjande av gränssnitt som mer fördelaktigt. Det förutsätter naturligtvis att man snabbt rättar upp problemen.

Hypotesen blir därmed; om ditt system är under utveckling (eller på andra sätt utsatt för stora förändringar), exekvera dina automatiska tester ofta och nyttja systemets gränssnitt för att generera din testdata.

I slutändan gäller det dock att vara pragmatisk och inte låta religös övertygelse styra.

 

 

 

 

Tankar om MBT och testdata

Typewriter-2

På mitt nuvarande uppdrag ska en befintlig webbapplikation ersättas med en ny för att förenkla underhållet. Funktionaliteten ska vara densamma och GUI behålls till största delen oförändrat.

Detta är alltså vad vi inom test ställs inför. Sedan tidigare finns ett stort antal automatiserade tester som är skapade mha Model Based Testing där huvudfokus varit att verifiera de viktigaste affärsflöderna.

Vi vet även att det är väldigt viktigt att kontrollera att alla beräkningar är korrekta i den nya webbapplikationen samt att rätt inmatningsfält populeras i alla formulär. Via ett administrations gränssnitt kan man styra vilka inmatningsfält som skall visas mha olika konfigurationsparametrar.

Vårt angreppssätt för att erhålla så heltäckande test som möjligt har varit att variera grunddata via administrationsgränssnittet och sedan ange olika indata i formulären. Eftersom vi antar att den befintliga applikationen räknar rätt är det lätt att spara undan resultatet och använda detta som facit i den nya applikationen.

Hur strukturera vårt testdata?

Det finns som vanligt flera lösningar på ett och samma problem. Man kan spara informationen i en databas eller lägga en del data i modellen. Vi har valt att spara data i Excel där önskad rad hämtas med hjälp av en iterator i modellen. Med Excel blir det också väldigt enkelt att snabbt få en överblick i resultatfilen och se vilka värden som erhålls i gamla och nya applikationen

Slutsats:

Det känns som vi hittat en väldigt effektiv lösning där man kunnat återanvända bef modeller och automatiseringsskript som efter mindre modifieringar stöder testdatahantering med Excel. Det är dessutom väldigt enkelt att bygga på med mer omfattande tester och även verifiera felfall. Här är det dock inte lika lätt att tolka resultatet eftersom just felhanteringen kommer att skilja sig mellan nya och gamla applikationen.