In een ontwikkelomgeving waar men gebruik maakt van geautomatiseerde testtools blijkt het veranderen van de testdata na een programmawijziging zeer veel inspanning te vergen. In dit eerste artikel van een serie van drie, stelt J.E. van der Laan dat dit te voorkomen is door het gebruik van relationele databases.
Steeds meer ontwikkelafdelingen oriënteren zich op de voordelen die het geautomatiseerd testen van de applicatie(-interface) kan bieden. De voordelen zijn dan ook legio. De benodigde inspanning voor het testen wordt gedecimeerd, de hoeveelheid testen die binnen een bepaald tijdsbestek kunnen worden uitgevoerd is beduidend hoger, hetgeen leidt tot een betere kwaliteit van de software, enzovoort.
Deze voordelen lijken op het eerste gezicht voldoende reden om snel op geautomatiseerd testen over te gaan. Toch staan veel organisaties nog sceptisch tegenover het fenomeen cast (computer aided software testing) en de organisaties die wel overgaan op het gebruik van cast blijken na enige tijd toch vaak voor problemen te komen staan.
Wat is er aan de hand?
Bij veel van de huidige geautomatiseerde testtools voor software kost het onderhoud van de gecreëerde testdata (te) veel inspanning. Vooral na een programmawijziging kost het wijzigen van de testdata veel capaciteit. Het probleem is dat veel van de huidige generatie testtools de testdata, die op basis van de bestaande applicatie worden ‘gecaptured’ of vastgelegd, opslaan in één of andere vorm van scripts. In deze scripts wordt bijvoorbeeld het scherm beschreven met de coördinaten van de velden, de invoer in de velden, de procestoets (‘Enter’ of een functietoets) en de verwachte uitvoer. Tijdens het uitvoeren van de test wordt datgene wat op het scherm verschijnt vergeleken met hetgeen in de scripts is vastgelegd; de invoer en procestoets worden ingebracht, waarna de uitvoer weer vergeleken wordt, enzovoort.
Fysiek bestaat een script meestal uit een flat file ofwel een tekstbestand.
Dit is een bekend principe bij het testen. Voor iedere test van een bepaalde functie wordt een script aangemaakt. Het aantal scripts kan op deze wijze al snel oplopen. Wanneer er nu een programmawijziging plaatsvindt, waardoor er in het scherm iets wijzigt, moeten alle scripts worden aangepast om deze wijziging te kunnen testen zonder al meteen een fout te constateren.
Neem het volgende voorbeeld. Een functie bestaat uit twee schermen. Hiermee kunnen alle medewerkers uit de database worden geselecteerd wier naam begint met een bepaalde letter. Het eerste scherm bevat slechts één invoerveld van één positie, zie figuur 1. In dit veld mag een letter (van A tot Z) worden ingevoerd, welke wordt bevestigd met
Om dit zeer eenvoudige programmaatje te testen zijn minimaal negen testgevallen aan te wijzen, namelijk:
1. invoer a [Enter] alleen namen beginnend met een A.
2. invoer z [Enter] alleen namen beginnend met een Z.
3. invoer A [Enter] alleen namen beginnend met een A.
4. invoer Z [Enter] alleen namen beginnend met een Z.
Deze eerste 4 testgevallen representeren de correcte invoer en uitvoer. De tussenliggende letters b t/m y vallen in de zelfde verwerkingslogica, en men kan er van uitgaan dat deze correct afgehandeld zullen worden. Dit noemt men de equivalentieklasse.
De volgende testgevallen testen het gedrag wanneer er incorrecte invoer wordt gegeven. Het programma dient te reageren met een foutmelding.
5. invoer 0 [Enter] foutmelding
6. invoer & [Enter] foutmelding
7. invoer Å [Enter] foutmelding
8. invoer spatie [Enter] foutmelding
9. invoer niets [Enter] foutmelding
Tevens zouden alle functietoetsen inclusief de PageUp en PageDown toetsen getest moeten worden.
De eerste vier testgevallen dienen allemaal in een apart testgeval opgenomen te worden. Alle testgevallen met incorrecte invoer kunnen in één testgeval worden ondergebracht.
Op basis van dit eenvoudige programma zijn er dus al minimaal vijf testgevallen, die in vijf aparte scripts volledig beschreven worden.
Is er sprake van een simpele wijziging, zoals het toevoegen van een beschrijving van de toegestane invoerwaarden bij het veld (figuur 3), dan dienen dus al vijf scripts te worden aangepast. Het script met de incorrecte invoerwaarden bevat dan minimaal vijf maal een beschrijving van het scherm.
Aan iedere schermbeschrijving in de scripts dient dus de tekst inclusief schermcoördinaten te worden toegevoegd. Deze toevoeging dient meestal handmatig te geschieden, door het script met behulp van een editor te wijzigen. Een kleine wijziging, zoals hier beschreven, vergt dus al een stevige inspanning.
Wijzigingen invoeren
Nu zijn er ook testtools die de mogelijkheid bieden om de tijdens de test geconstateerde verschillen automatisch in de testdata op te laten nemen, zodat de testdata de nieuwe situatie beschrijven. Men hoeft de wijzigingen dan niet meer handmatig in te voeren.
In het geval van de simpele schermwijziging kan dit inderdaad uitkomst bieden. Het aantal handelingen wordt dan beperkt tot het uitvoeren van de test en het initiëren van de automatische wijziging (waarbij men er wel op moet letten dat er geen echte fouten meegenomen worden).
Wanneer de wijziging echter iets verder gaat, is deze functionaliteit al niet meer voldoende. Stel bijvoorbeeld dat de volgende wijziging wordt doorgevoerd.
De procestoets om de ingegeven selectie te verwerken wijzigt van
Er zit maar één ding op: de scripts handmatig wijzigen of de test opnieuw opnemen. Dit gaat weer de nodige inspanning kosten. Hieruit blijkt dat een ogenschijnlijk simpele wijziging in de applicatie een vergaande wijziging met betrekking tot de testdata tot gevolg heeft. Stel je eens voor dat de wijziging in een grote applicatie plaatsvindt en doorwerkt in meerdere functies! Geen wonder dat veel ontwikkelaars sceptisch tegenover dit soort tools staan.
Relationele database
Is hier nu geen oplossing voor?
Jawel, maar dan moeten we even naar de kern van het probleem. Vroeger (en in sommige omgevingen nog steeds) trad een soortgelijk probleem op bij alle data. Gegevens werden opgeslagen in zogenaamde flat files: sequentiële bestanden waarin de gegevens als regels (records) met een sleutel (naam, nummer) werden opgeslagen. Een record in zo’n bestand bevatte een aantal kenmerken van een entiteit, die van belang waren voor een bepaalde toepassing. Voor een andere toepassing kon een bestand bestaan met (gedeeltelijk) andere gegevens over dezelfde entiteit. Wanneer echter bepaalde kenmerken van een entiteit veranderden (bijvoorbeeld adresgegevens) moest dit in alle bestanden apart worden bijgewerkt. De consistentie van de bestanden liep hierdoor gevaar. En dit is wat er met onze testdata eveneens gebeurt. Om ondermeer voor dit probleem een oplossing te bieden is de relationele database ontwikkeld. Hierin worden alle gegevens betreffende een entiteit eenmalig opgeslagen, waarna alle toepassingen gebruik maken van dezelfde gegevens door middel van het beschrijven van de gegevensrelaties.
Veel organisaties zijn overgegaan op het gebruik van relationele databases en konden hierdoor het geschetste probleem voorkomen.
Als het principe van de relationele database zo’n goede oplossing bied voor het consistentieprobleem, kan dit dan ook niet worden toegepast voor onze testdata?
Jazeker, zeer goed zelfs. Het opslaan van de testdata in een relationele database biedt een scala aan voordelen.
Wanneer er bijvoorbeeld een testgeval voor de hiervoor beschreven applicatie wordt opgenomen zal dit als volgt gebeuren.
Met behulp van het capture-principe wordt het huidige gedrag van de applicatie opgenomen. Tijdens opname wordt ieder afzonderlijk scherm dat wordt gepasseerd éénmalig in de database opgeslagen, inclusief de velden. De ingevoerde data (keystrokes) en de uitvoergegevens die op het scherm zichtbaar zijn (response) alsmede de toetsaanslag die de verwerking van het scherm initieert, worden op die plaats in de testgeval aan het scherm gerelateerd. Wanneer hetzelfde scherm in hetzelfde (of een ander) testgeval opnieuw wordt gepasseerd, worden de keystrokes, response en de procestoets van dat moment wederom gerelateerd met het reeds in de database opgeslagen scherm.
Zo maakt ieder testgeval gebruik van dezelfde schermen. Om nu de toevoeging van de schermtekst zoals beschreven in het eerste voorbeeld (zie figuur 3) in de testdata te reflecteren kan men volstaan met het wijzigen van één scherm in de testdata. Alle schermen in de testgevallen die dit scherm passeren verwijzen immers naar dezelfde schermbeschrijving!
Op basis van dit principe is het zeer eenvoudig een Wysiwyg-wijzigingsfunctie te creëren om de gebruiksvriendelijkheid te bevorderen.
Om de procestoetswijziging van
‘Zoek procestoets:
(eventueel ‘in de volgende testgevallen:—(met/zonder bevestiging’).
Deze functie kan eenvoudig worden uitgebreid voor toetsaanslagen, response en zelfs schermteksten.
Een bijkomend voordeel van de relationele database en de afwezigheid van scripts is de mogelijkheid om testgevallen eenvoudig te groeperen tot grotere eenheden (test drivers) door enkel relaties te beschrijven. Wanneer men de test driver start, worden alle onderliggende testgevallen uitgevoerd. Boven de test driver kunnen weer hogere niveaus worden aangelegd.
Wanneer een testtool dus gebruik maakt van een relationele database om de testdata in op te slaan, wordt zeer veel inspanning bespaard in het onderhoud van de testdata. Dit maakt het voor organisaties die het gebruik van geautomatiseerde testtools overwegen, zeker interessanter om in deze technologie te investeren.
In een tweede en derde artikel wordt ingegaan op respectievelijk het interpreteren en valideren van testuitvoer door geautomatiseerde tools voor het testen van software, en de psychologische aspecten van het testen.
Jan Erik van der Laan is werkzaam als consultant bij QA Consultant te Joure.