maandag 27 april 2015

Testautomatisering: Elementen herkennen in een veranderende webapplicatie

Een van de meest gehoorde problemen bij testautomatisering van een webapplicatie. Je maakt via record-and-play functionaliteit een werkend testscript. Je past hem aan en hij werkt. Er komt een nieuwe versie van de applicatie.... en je testscript werkt niet meer. De knop staat op een andere plaats. Het de naam van het invoerveld is plotseling anders. De getoonde lijst heeft een andere sortering. En jij mag het weer gaan aanpassen.

Er zijn echter manier om dit soort problemen flink te verminderen. Maar de belangrijkste les om dit doel te bereiken, vraagt misschien voor sommige testers een flinke stap: ga niet uit van record-and-play. Als record-and-play een item voor je vastlegt in het testscript, zal dit te vaak op een manier zijn die niet geschikt is voor een veranderende applicatie. Daarom zal je als tester zelf moeten leren hoe je objecten op een pagina uniek kan identificeren. Tenminste, als je je testscript meer geschikt wil maken voor veranderingen.

Leer de HTML-structuur

De eerste stap is het leren lezen van een HTML pagina. Je hoeft niet alles te snappen, je moet wel weten hoe je de structuur van een HTML pagina kan herkennen. En je moet weten hoe je in de HTML code een specifieke knop of afbeelding kan terugvinden. Elke programmeur kan je vertellen hoe je dit doet en welke tools hiervoor geschikt zijn. Om deze blog niet te lang te maken, zal ik dit daarom niet beschrijven.

Leer zelf elementen herkennen

Voor de tweede stap moet je je testautomatiseringstool beter leren kennen. Leer hoe je tool elementen kan identificeren en leer hoe je deze identificatie zelf aanpast. Hieronder staan de leerdoelen beschreven, die je, naar mijn ervaring, bij voorkeur moet kunnen toepassen. Zelf heb ik meestal gebruik gemaakt van Selenium IDE. Bij Selenium IDE kan je gebruik maken van XPath of CSS-selectors om elementen te vinden. Voor onderstaande leerdoelen zal ik dan ook alvast het XPath of de CSS-selector meegeven.

1. Een deel van de waarde van een attribuut selecteren
Een voorbeeld van een attribuut met een waarde is type="submit" . Het kan echter handig zijn om bij langere waardes slechts een deel van de waarde te kunnen selecteren. Dit is bijvoorbeeld verstandig als de naam of het id, de meest gebruikte attributen bij testautomatisering, niet bij elke versie van de applicatie dezelfde waarde hebben. Sommige programmeertalen geven een element bij elke versie een ander id of een andere naam. Dit gegenereerde deel is vaak herkenbaar door een numerieke waarde of een onleesbaar stukje met letters en cijfers. Maar bijna altijd is er ook een deel wat de functionaliteit van het element beschrijft, b.v. id="btn_zoek939"  of id="input_G4K9L_surname". Als je dit deel van de waarde gebruikt, is het niet nodig om elke versie je testscript aan te passen.

HTML-code = <INPUT id="input_G4K9L_surname">
XPath = //INPUT[contains(@id,'surname')]
CSS-selector = input[id*=surname]

2. Tekst herkennen
Tekst op een HTML pagina is niet bepaalt een vast gegeven. Teksten worden regelmatig aangepast. Daarom is tekst vaak niet verstandig om te gebruiken bij het identificeren van elementen. Er zijn echter uitzonderingen op deze regel. De meest voorkomende hiervan is "selecteren op basis van gegevens". Soms moet je in b.v. een filterlijst of een zoekresultatenlijst altijd een bepaalde waarde selecteren of openen. Bijvoorbeeld in een filterlijst wil je altijd "Duitsland" selecteren. Of in een tabel met aangeboden producten wil je altijd de stofzuiger openen. Als deze concrete waarde nergens in een attribuut terug te vinden is, dan zal je het element voor deze keuze aan de hand van de tekst moeten herkennen.

HTML-code = <TD>Stofzuiger</TD>
XPath = //TD[contains(text(),'Stofzuiger')]

3. Het zoveelste attribuut selecteren
Als er op een pagina lijsten voorkomen, bijvoorbeeld tabellen voor een productkeuze of checkboxlijsten voor filteren, is het soms moeilijker om een element te vinden. Als je altijd de eerste optie kiest, zal dit vaak niet veel problemen opleveren. Maar het is regelmatig ook verstandig om (ook) de derde of zevende te kiezen. Dan moet je kunnen tellen om een element te herkennen.

In een andere situatie heb je soms elementen onder elkaar, die hele verschillende gegevens bevatten, maar op geen enkele manier uniek te herkennen zijn. In een tabel staat bijvoorbeeld in de eerste kolom de naam van een persoon, in de tweede kolom het adres en in de derde kolom het telefoonnummer. Maar alle teksten staan in het standaard HTML-element voor een kolom, namelijk <TD>. Ook dan moet je, om het adres te kunnen controleren, weten hoe je het tweede <TD> element moet selecteren. In onderstaande uitwerking wordt van deze situatie uitgegaan.

HTML-code= <TR><TD>Jan de Visser</TD><TD>Damplein 4</TD><TD>0123-456789</TD></TR>
XPath = //TR/TD[2]
CSS-selector = tr>td:nth-of-type(2)

4. Terug naar de parent
Het is verstandig om de identificatie van een element zo dicht mogelijk bij het element zelf te houden. Hoe dichter je namelijk bij het element blijft, hoe kleiner de kans dat wijzigingen in de opmaak een wijziging in je testscript veroorzaken. Als je de vierde rij in een tabel selecteert, begin je daarom bij voorkeur bij het selecteren van de tabel en ga je daarna naar de vierde rij.

Maar stel dat je vanaf het geselecteerde element steeds naar het element kijkt waar dit element een deel van uitmaakt, ook wel de parent genoemd. En daarna weer naar de parent van de parent. En dan naar de parent van de parent van de parent. Soms kunnen al die elementen zo algemeen beschreven zijn en zo vaak voorkomen, dat je een hele lange rij van parents hebt, voor je een uniek element kan vinden. Dan kan het verstandig zijn om niet naar de parent te kijken, maar naar een ander uniek element in de buurt.

Stel dat je bijvoorbeeld een samenvatting van een bon hebt. Deze staat in de tabel weergegeven. Onderaan de bon staat de tekst "Totaalprijs €14,73". De tekst "Totaalprijs" staat in de eerste kolom. Het bedrag in de tweede. Je kan er dan vrij veilig vanuit gaan dat de tekst "Totaalprijs" altijd voor het bedrag zal blijven staan. En de tekst "Totaalprijs" zal waarschijnlijk ook niet zo vaak wijzigen. In je testscript kan je dan het element met de tekst "Totaalprijs" selecteren. Hierna ga je naar de parent van dit element. Vervolgens ga je dan naar het element van de totaalprijs.

HTML-code = <TR><TD>Totaalprijs</TD><TD>€14,73</TD></TR>
XPath = //TD[contains(text(),'Totaalprijs')]/../TD[2]

Blijf proberen

Als je de HTML-structuur kan lezen en de bovenstaande leerdoelen in de vingers hebt, blijf dan proberen de beste identificatie voor je elementen te vinden. Wanneer je op een bepaalde plek veel moet wijzigen, kijk dan eens of de identificatie anders kan. De belangrijkste algemene tips zijn:

1.  Blijf zoveel mogelijk van beschrijvende attributen uitgaan, die een zo klein mogelijke kans op wijzigingen hebben

2. Blijf zo dicht mogelijk bij het element zelf, door zo weinig mogelijk naar een parent te gaan

De oplossing blijft voor elke applicatie anders. Maar er is altijd wel een oplossing.









zondag 19 april 2015

Het testeiland - Veilig van de Agile eisenwereld

Als tester werk je in de Agile wereld in een Agile team. Of twee of drie of vier. Of soms zelfs in nul teams en je bent adviseur. Je doet je werk in een rol, neem bijvoorbeeld functioneel tester. Of juist in meerdere rollen Je moet kunnen programmeren. Of je moet niet willen programmeren. Je moet andere testvaardigheden hebben, zoals performance testen, keten testen. Of je moet dit vooral aan anderen overlaten. Ik merk dat de rol van tester nu per team en per bedrijf erg sterk kan verschillen. En daarmee ook de eisen waaraan je moet voldoen. Wat is het dan heerlijk als je kan zeggen: "Ik ben functioneel tester van applicatie A, dus wat ik doe is applicatie A functioneel testen. Niets meer en niets minder" Lekker veilig op je testeiland. Wel zo duidelijk!

Ik ben zelf ook van mening dat de invulling van de rol van tester in de Agile wereld een stuk duidelijker kan. Tester is niet de meest eenvoudige rol in een Agile team. Het werk is niet altijd dagvullend. Of is meer dan dagvullend. Voor elk team een tester is niet in elk bedrijf mogelijk en twee vaak al helemaal niet. Ook bij Agile komt het vaak voor dat het meeste testwerk aan het eind ligt, de werkverdeling evenwichtig verdelen blijft moeilijk. En daarnaast heb je nog het terechte streven naar onafhankelijkheid van de tester, onder de noemer "Test niet je eigen werk". Als je in zo'n situatie je testeiland hebt gevonden, blijf je daar het liefst eeuwig op wonen.

Hoe de rol van tester beschreven kan worden, er zijn eerlijk gezegd veel ideeën over. Ik heb zelf mijn eigen ideeën, te splitsen in twee onderdelen: je rol en je kennis. Om te beginnen met je rol: naar mijn mening vervul je die rol maar in maximaal één scrumteam. Je bent in maximaal één scrumteam actief als tester, bij andere teams kan je wel betrokken zijn in een adviserende of begeleidende rol. Dit is vooral om conflicterende belangen te voorkomen. Als twee teams allebei je hulp nodig hebben om op tijd het werk af te ronden en je hebt daar de tijd niet voor, wordt je een impediment. Dan wordt het een situatie "welk team is het belangrijkste". Een situatie waar je niet in wil komen, want teams moeten zonder afhankelijkheid van elkaar hun werk op tijd kunnen afronden.

Ook in een adviserende of begeleidende rol moet je daarom geen impediment kunnen worden. Bij deze rol hoort daarom bij voorkeur geen uitvoerend werk, maar zeker geen uitvoerend testwerk. Adviserend heeft daarom de voorkeur. Maar als je specifieke kennis hebt, die het team nodig heeft, dan moet het werk vooral in de testvoorbereiding of testautomatisering liggen. Testscripts schrijven, die vervolgens binnen het team op het door hun gewenste moment uitgevoerd kunnen worden. Testwerk automatiseren, wat vervolgens binnen het team op het door hun gewenste moment uitgevoerd kan worden.

Wat kennis betreft moet het bij jou en je team liggen hoe jullie daarmee omgaan. Je moet in een Agile team starten met één bepaalde specialiteit, vaak functioneel of technsich tester. Je moet daar als basis beginnen met de kennis die voor die specialiteit nodig is. Dat is je testeiland. Datgene waar het team altijd op terug kan vallen en jijzelf ook.

Maar vervolgens moet je bereid zijn ook andere kennis op te doen of in de praktijk te brengen. En het Agile team moet bereid zijn om te kijken naar de beste oplossing voor de gevraagde kennis. Als je in een team bijvoorbeeld geen performancetester hebt, kan deze taak zowel door een tester als door een programmeur uitgevoerd worden. Het is maar net wie het meest geschikt is. De programmeur heeft bijvoorbeeld weinig werk in zijn specialisatie. Of de tester heeft eerdere ervaring met performance testen. Dit kan bekeken worden tijdens het opstellen van de planning. Of besproken worden bij een evaluatie. Wat het beste bij het team past, als je er maar regelmatig naar kijkt. Wanneer die betreffende programmeur of tester vervolgens uit het team gaat, houdt dit niet in dat de nieuwe programmeur of tester automatisch ook de performancetest moet uitvoeren. De situatie is anders, dus het team moet opnieuw kijken wat de beste oplossing is.

De beste oplossing kan ook zijn dat er leertijd gepland moet worden tijdens de planning. Tijd waarin een teamlid, bijvoorbeeld een tester, nieuwe kennis van bijvoorbeeld een tool opbouwt. Zo kan het best dat je als tester leert om te programmeren, bijvoorbeeld om Unit testen te schrijven. En misschien zelfs om stukjes applicatie te bouwen. Hier moet je mee oppassen, maar moet je zeker niet volledig tegenhouden. Wat in zo'n situatie belangrijk is, is dat er iemand anders in het team geschikt is om het door jou gebouwde werk te testen.

Het belangrijkste is dat het bedrijf niet teveel bepaalt welke rollen en kennis jij binnen een Agile team moet hebben. En bereid is om mensen de tijd te geven zich andere rollen en kennis eigen te maken, als het Agile team hierdoor effectiever kan gaan werken. Het team zelf en alle teamleden los, dus ook de tester, moeten bereid zijn nieuwe kennis op te doen. Ook als deze niet direct in hun specialiteit of rol valt. De beste momenten om hierbij stil te staan zijn planningen en evaluaties. Maar het allerbelangrijkste is en blijft om je niet terug te trekken op je (test)eiland. Hoe verleidelijk en veilig dat soms ook is.

zondag 12 april 2015

Acceptatiecriteria - Het nieuwe contract?

Eerst had je het functioneel ontwerp, bij voorkeur van punt tot punt vastgelegd en met handtekening eronder. Dat willen we vaak niet meer. Maar toch heb je enige houvast nodig, dus hebben we nu zeer regelmatig acceptatiecriteria. Geen handtekening, maar wel een meetpunt om via een acceptatietest alsnog een definitief akkoord te krijgen. Maakt dit van acceptatietesten de nieuwe handtekening onder een contract? Zijn de acceptatiecriteria het nieuwe contract? En moet je dat wel willen?

Zowel bij programmeurs en testers is heel veel vraag naar duidelijke specificaties. Logisch, de informatie uit het functionele ontwerp heb je ook nu nog nodig om tot een goede applicatie te komen. Ook om duidelijk te kunnen afspreken wat wel en wat niet geaccepteerd gaat worden. Daarom gaan we het volledige FO als scrumteam vaak proberen op te vangen in de beschrijving van de story. En met enige grote regelmaat gebeurt dit via de acceptatiecriteria in de story. Ook als tester heel handig, want je weet zeker dat je alle informatie waarop je moet testen in de story staat.

Maar is de acceptatietest nu eigenlijk wel een acceptatietest? Een acceptatietest heeft in mijn ogen voornamelijk als doel om te bepalen of het opgeleverde werk, vanuit de business en de klant gezien, naar productie kan. Dus de acceptatiecriteria moeten hier een afspiegeling van zijn. En zolang het project op tijd loopt, lijkt dit ook het geval. Totdat er tijdnood komt. Dan blijken veel lijsten met acceptatiecriteria opeens niet zo belangrijk te zijn.

Acceptatiecriteria zijn bijvoorbeeld doorgeslagen in detail. Stel je hebt een invoerveld waarbij het acceptatiecriterium is: "Bij een geldige waarde in het veld moet naast het veld een groen vinkje getoond worden". Stel nu dat er een zwart vinkje is gebouwd. Er is geen tijd meer, dus het invoerveld gaat naar productie of het invoerveld gaat niet naar productie. Het is het enige veld in een scherm, de validatie is verder goed. Er is geen enkele reden om het af te keuren, behalve de kleur van het vinkje. Neem maar van mij aan, het invoerveld wordt naar productie opgeleverd. Zelfs als het hele vinkje niet aanwezig zou zijn, het invoerveld gaat zeer waarschijnlijk naar productie. Want wat wil de gebruiker? De gebruiker wil geen groen vinkje. De gebruiker wil een invoerveld waaraan duidelijk te zien is of de ingevoerde waarde fout of goed is. Of dit wordt aangegeven met een vinkje, met een foutmelding met een rode rand om het invoerveld, de gebruiker maakt het niet uit. Waarom maakt het in de acceptatiecriteria dan wel uit?

Wat ook wordt toegepast in de acceptatiecriteria is de verwijzingen naar andere documenten. Het gebouwde scherm moet voldoen aan het design. Het moet voldoen aan het interactie-ontwerp. Of misschien zelfs voldoen aan het functioneel ontwerp. Tja, vroeger leek datgene wat in productie ging al niet volledig op het ontwerp. Zou dat nu zoveel anders zijn? Ik heb in ieder geval nog geen acceptatietest meegemaakt waarin de acceptatietester het ontwerp tot op de letter of tot op de pixel ging controleren. En bij het eerste kleine verschil zei: "Nee, dit gaat dus niet naar productie"

Het probleem is naar mijn mening dat we graag blijven bij het oude, vertrouwde, veilige watervalprincipe van een contract met handtekening. We bouwen wat voor ons is vastgelegd. Niets meer en niets minder. Als we het niet gebouwd hebben en het was ook niet vastgelegd dat we het moesten bouwen, dan is het extra. Vroeger extra tijd en geld, nu een extra story. Want het blijft dan toch een vooruitgang? Als iemand wilde afwijken was dat bij waterval eigenlijk niet toegestaan. Nu kan je de story gewoon direct prioriteren, je mag dus afwijken. Dus vooruitgang.....

Als gevolg hiervan zie ik ontwikkelaars die al vijfentwintig keer een datum invoerveld hebben gebouwd tot in detail vragen hoe het veld moet werken. Het antwoord "Kan mij wat schelen, als ik maar alleen geldige datums kan invoeren" is vanuit de business vaak het enige juiste antwoord. Maar dat antwoord mag niet. Want zo kunnen we niet bouwen of testen. Hoe moeten we anders antwoorden krijgen? Tja..... bedenk zelf het antwoord? Als je al vijfentwintig keer een datum invoerveld hebt gebouwd of getest, kan je dan niet zelf bedenken welke invoercontroles nodig zijn om aan het acceptatiecriterium te voldoen?

Maar hoe kan je dan weten of het gebouwde in een keer naar wens is? Tja, dat weet je niet. Misschien heb je een zwart vinkje gebouwd en wordt de vraag gesteld "Kan dat vinkje niet groen zijn?" Waar het om gaat is het vertrouwen dat je in je scrumteam en met de business dit soort zaken via overleg kan oplossen. Overleg veel, tijdens de bouw, tijdens de test, tijdens de acceptatietest. Als je eerst te horen krijgt "Maak me niet uit" en later "Ik wil het toch anders", zie dat niet als een nadeel van de methode. Dit is juist een van de belangrijkste redenen waarom we geen functioneel ontwerpen meer schrijven, waarom we meer Agile willen werken. Vaak weet je pas echt wat je wil, als je het voor je ziet en je er werkelijk doorheen kan klikken.

Hoe ga je je hiermee om tijdens een acceptatietest? Overleg vooral goed of het verstandig is de nieuwe wensen direct op te pakken of door te schuiven. De sprint mag niet in gevaar komen. En het blijft: als het gebouwde aan de acceptatiecriteria voldoet, mag een wijziging in het gebouwde niet de oplevering in gevaar brengen. Wanneer dit wel het geval is, gaat het gebouwde zoals het is naar productie. Zonder wijzigingen. Dit klinkt streng, maar eigenlijk willen bijna altijd alle partijen dit. Ieder vanuit hun eigen belang. Mits de acceptatiecriteria een goede weergave waren van de echte wensen.

Maak daarom van een acceptatietest een test waarin de acceptatiecriteria altijd volledig gecontroleerd worden en leiden tot een betrouwbare acceptatietest. Zorg ervoor dat de acceptatiecriteria weergeven wat de business wil. En maak van de acceptatiecriteria geen weergave van wat het scrumteam wil weten. Dit zijn twee verschillende zaken. We moeten dan ook allemaal, hoe moeilijk het soms ook is, leren om deze twee soorten beschrijvingen los van elkaar te gaan zien. En het belangrijkste daarbij is vertrouwen. Vertrouwen dat je er zonder contract met handtekening, maar met overleg, ook wel samen uit gaat komen.

maandag 6 april 2015

Sprint 0 of "Waarom wil je niet alles automatiseren"

Sprint 0, de start van een nieuw project. Voor mij een net afgerond traject. Tot mijn eigen verbazing bestaat de voorbereiding van een nieuw project voor mij vaak uit het beantwoorden van de vraag "Waarom wil je niet alles automatiseren?" Want juist bij Scrum en Agile wil ik niet alle testen automatiseren. Maar het lijkt wel of zo'n uitspraak in tegenspraak is met alles waar Scrum en Agile voor staan.  Heb ik dan echt zo weinig kennis van Agile en Scrum dat ik die verwachting voor testautomatisering nergens terug kan vinden?

Bij de start van een nieuw project is het altijd verstandig om stil te staan bij welke testen je wil automatiseren en welke niet. Performance gaat al snel naar automatisch testen. Testen van vormgeving vaak weer niet. En bij alle testen geldt ook voor mij: bij voorkeur automatisch. Maar dan moet het wel een meerwaarde hebben. Ik blijf er bij: het automatiseren van testen is geen doel, het is een middel om goede software te bouwen en te behouden. Levert het daaraan niet of nauwelijks een bijdrage, dan automatiseer je niet.

Dat is makkelijker geschreven dan bedacht. Want levert testautomatisering niet altijd een bijdrage aan het bouwen van betere software? Nee, naar mijn mening niet. Om een extreem voorbeeld te gebruiken: je kan vijfentwintig keer in een testcase testen of een knop de gebruiker van scherm 1 naar scherm 2 brengt. Maar als dat het enige is wat je test, heeft de test geen meerwaarde. De meerwaarde is er wel als de knop je van een invoerscherm naar een controlescherm brengt, waarin de ingevoerde gegevens opnieuw getoond worden. En elke testcase voor een bepaald invoerveld bijvoorbeeld een minimum of een maximale waarde test.

Dit kunnen de meeste testers zelf wel bedenken. Maar hoe zit het in deze situatie? Stel je hebt al een test, waarin je controleert of de waarde van een veld na de klik op een knop in het volgende scherm goed wordt meegegeven.  Heb je dan de testcase "Als de gegevens correct zijn, wordt na een klik op de knop het controle scherm getoond" nog wel nodig? Als aan deze eis niet voldaan wordt, zal ook het controleren van de waarde falen. Dat maakt de test overbodig. Een reden waarom ik nauwelijks testcases opstel en zeker niet automatiseer, die de navigatie tussen schermen controleren.

En stel nu dat je de vormgeving van een applicatie nog handmatig test. En het genoemde scherm krijgt bijna elke sprint  wel extra velden. Dus bijna elke sprint worden het invoerscherm en het controlescherm bij het handmatig testen van de vormgeving meegenomen. Neem maar rustig van mij aan, dat het niet uitmaakt wie het scherm test. Iedereen zal het laten weten als na een klik op de knop het controlescherm niet verschijnt. Automatisch testen voor de eerder beschreven testcase heeft in die situatie nauwelijks meerwaarde. Die paar keer dat het scherm in een sprint niet wordt aangepast, kan je hem wel handmatig testen.

Toch zijn er situaties waarin dubbel automatiseren wel een meerwaarde heeft. Als al tijdens het programmeren kan worden vastgesteld dat de knop niet meer werkt, kan de programmeur dat direct aanpassen. En dat heeft zeker voordelen. Maar vervolgens kan de knop best wel eens alleen werken in Firefox en niet in IE. Of de knop werkt ten onrechte niet, als het invoerveld "Geboortedatum" is leeg gelaten bij de persoonsgegevens. Vaak omdat de programmeur een foute aanname heeft gemaakt. Een aanname die herhaald is in zijn eigen automatische testen. In alle beschreven gevallen heeft de testcase dubbel automatiseren een meerwaarde. In het ene geval test je nu in een andere "omgeving", namelijk IE. In het andere geval is de automatisering om het werk te laten testen door een persoon die het werk niet gebouwd heeft. Een testprincipe wat ook bij testautomatisering erg belangrijk is.

Met een eenvoudig voorbeeld klinkt het misschien allemaal zo simpel en begrijpelijk. Ik heb in ieder geval mijn best gedaan om het zo begrijpelijk mogelijk op te schrijven. Maar zeker aan de start van een project is het dat niet, de te testen functies zijn zeker niet altijd zo simpel. Probeer het wel. Bedenk welke overlap de testen hebben. Sta stil bij de directe testen, maar ook de indirecte testen, zoals het klikken op knoppen bij het controleren van de vormgeving. Bedenk of het dubbel testen, al of niet automatisch, een duidelijke meerwaarde heeft. En maak je niet teveel zorgen over fouten. Als je te veel of te weinig meeneemt in je automatische test, je project is Agile. En dat betekent dat niet alleen de business, maar ook jij van mening mag veranderen.