Hoe testen slimmer, sneller én minder kan

Met al die verschillende testsoorten en werkwijzen ontstaat wel een nieuw probleem. Elk team heeft zijn eigen keuzes gemaakt waar het gaat om teststrategie, -tools en -frameworks. Hoewel er al heel veel testen zijn en dat aantal elke sprint gestaag meegroeit met alle nieuwe features, lijkt het inzicht in de kwaliteit paradoxaal genoeg alleen maar kleiner te worden. Een volledige regressierun van de inmiddels duizenden tests duurt geen minuten meer, maar uren. Er valt eigenlijk altijd wel wat om; soms vanwege de wijzigingen van een ander team, soms vanwege omgevingsinstabiliteit, maar soms ook om onduidelijke redenen.

Terwijl “vroeger” testen een kostbare bezigheid was en er dus heel veel nagedacht werd over een goede teststrategie, lijkt het er in ons project meer op dat als er iets geautomatiseerd kan worden dat ook gedaan wordt. En als iets geautomatiseerd is, het dan altijd belangrijk blijft. Dat het resultaat hiervan is dat onderhoud op de testset steeds duurder wordt, de overlap in dekking van tests toeneemt en de regressietest steeds langer gaat duren wordt blijkbaar voor lief genomen.

Wanneer onvoldoende is nagedacht over welke zaken hoe, waarom en wanneer (niet) getest moeten worden, ontstaan vaak situaties waar het proces terugvalt naar een proces van hokjes en hand-overs. De ‘scheidingslijnen’ vallen dan vaak op de grenzen van de testsoort: ontwikkelaars zijn verantwoordelijk voor de unittests, testers zijn verantwoordelijk voor systeem- en acceptatietests. Maar levert deze scheiding van verantwoordelijkheden binnen je team nu de meest effectieve teststrategie op?

In ons grote ICT-project schrijft de tester vooral tests tegen de UI en houdt vanwege het toenemende onderhoud steeds minder tijd over om naast het automatiseren van die controles ook nog zelf te testen. Geen van de ontwikkelaars weet hoe de geautomatiseerde end-to-end testen zijn opgebouwd en als de tester op vakantie is rijst de vraag of de release moet worden uitgesteld of dat de testen die week even kunnen worden uitgezet?

In alle trajecten waarin geautomatiseerd getest wordt, groeit de set van testgevallen mee met de hoeveelheid opgeleverde functionaliteit. Vaak treedt er ongemerkt al snel redundantie op in de aanwezige testen. Neem een front-end applicatie voor geregistreerde gebruikers. De loginfunctionaliteit zal misschien al in sprint 1 gebouwd zijn samen met een aantal tests die vaststellen dat we aan alle requirements in die story hebben voldaan. Een aantal sprints later echter zijn er tientallen nieuwe tests geschreven, die allemaal via het loginscherm door een flow heen lopen. Als de login niet werkt, vallen al deze tests om. Je kunt je afvragen hoe waardevol de login-tests uit sprint 1 nog zijn en of die waarde opweegt tegen de langere doorlooptijd en het extra onderhoud. Dit proces herhaalt zich keer op keer. Testscenario’s tonen impliciet veel meer aan dan waarvoor ze initieel misschien bedoeld waren. Maak daar gebruik van en gooi overbodige testen regelmatig weg!

Hebben we de afgelopen sprints misschien nieuwe inzichten opgedaan of functionaliteit opgeleverd die de bestaande testen zou kunnen verbeteren? Als team valt er veel te winnen door het gesprek over testen op gang te houden. Ook de product owner moet hierbij betrokken zijn: geautomatiseerde testen horen bij het product en het optimaliseren ervan op de backlog. Een snellere testset betekent sneller naar productie en dat levert business value op! 

Wanneer alle teamleden elkaars kennis gaan benutten en van elkaar begrijpen wat ze doen, wordt het mogelijk de verschillende testsoorten beter op elkaar af te stemmen. Het opleveren van de juiste applicatie is niet alleen de verantwoordelijkheid van de ontwikkelaar, net zo min als het opleveren van een goede testset alleen de verantwoordelijkheid is van de testspecialist. Van ‘samen werken’ naar ‘samenwerken’ is voor veel professionals nog een hele kunst, maar wel de enige manier om de hoeveelheid dubbele en nutteloze testen te verminderen. Is het nodig functionaliteit te controleren door middel van een front-end test? Of heeft de module die de logica bevat ook een API? Ziet een tester in dat zijn testgeval in milliseconden in een unittest uitgevoerd kan worden? En wie weet was die unittest er al? Andersom is het bijzonder prettig wanneer ontwikkelaars ook meedenken over nieuwe integratie- of end-to-end testen, die horen bij de functionaliteit die opgeleverd wordt.

Met meerdere releases per dag en de steeds groter wordende testset is het steeds lastiger om continu het overzicht te bewaren van wat er op enig moment getest zou moeten worden (zeker als je met 6 teams aan een product werkt!). De stap van samen werken naar samenwerken is noodzakelijk om dit overzicht te vergroten, maar dan nog is het lastig te bepalen wat de “optimale testset” objectief gezien is. Met een traditionele testadministratie redden we dat niet meer. We zouden onze teststrategie bijna real-time moeten stroomlijnen. Volgende week nemen we je mee in onze visie op hoe dat zou kunnen.

Augmented testing: Sluitpost van delivery?

In ons grote ICT project releasen we nog altijd meerdere keren per dag. We hebben het proces aardig op orde. Elke wijziging gaat door de pipeline, waarin de nieuwe versie na een geslaagde build op een testomgeving geplaatst wordt, zodat daar de geautomatiseerde regressietesten kunnen worden afgedraaid. We hebben een goede verdeling tussen unit-, systeem- en end-to-end testen en het hele team neemt gezamenlijk verantwoordelijkheid voor de kwaliteit van het product. Echter, na verloop van tijd begint het blokje ‘test’ in de pipeline toch wéér een bron van zorg te worden.

Wat wil het geval? Al onze test cases tezamen kennen inmiddels een doorlooptijd van ruim een uur. Omdat we nog vaker kleine wijzigingen willen opleveren, wordt inmiddels elke change zo snel en klein mogelijk gepusht naar het versiebeheersysteem en elke wijziging triggert een nieuwe build.

Op een regenachtige vrijdagmiddag werden op de valreep voor het weekend de laatste wijzigingen ingecheckt, zodat de marketingcampagne voor ons nieuwe product die avond in het reclameblok voor het 8-uur journaal veilig van start kon gaan. De CI server pakte de wijzigingen netjes op en de verschillende branches kwamen in een queue terecht. Na twee minuten bouwen en unit- en systeemtesten werd de eerste deploy naar een testomgeving gestart, waar de integratietesten gingen plaatsvinden. Een uurtje later waren de testen klaar en bleek er nog een kleine bug in te zitten, waardoor klanten geen bestellingen kunnen doen.

Een paar ontwikkelaars hadden voor de zekerheid alvast Chinees besteld en gingen direct aan de slag om een oplossing voor deze bug te implementeren. Een klein uurtje later was de bug opgelost en de wijziging gecommit. Om 7 uur werd de nieuwe versie van het component in de pipeline gezet.

Helaas was dit niet het enige team dat Chinees had besteld: er waren ook nog andere teams actief. Het werd dus een kwestie van achteraan aansluiten: als 3e in de rij ging het reclameblok van het 8 uur journaal bij lange na niet gehaald worden.

Er zijn een aantal dingen die je zou kunnen doen om je pipeline voor dit soort situaties te versnellen. Een belangrijke wijziging releasen via een “fast lane” zou in dit geval kunnen helpen. Niet elke release is even tijdkritisch ten slotte. Een andere oplossing is om meerdere componentreleases tegelijkertijd door de pipeline te halen: op die manier bouw je geen queue op. Een derde mogelijkheid is om componentreleases die geen productiecode wijzigen, niet (direct) naar productie te brengen (bijvoorbeeld enkel wijzigingen in testcode of documentatie). Dit kan een hoop pipeline ontstoppende releases voorkomen, die toch geen nieuwe features opleveren. Maar zelfs met deze (uitstekende) oplossingen wordt het halen van het juiste reclameblok nog steeds krap. Wat zouden we nog meer kunnen doen?

Als we kijken naar de totale doorlooptijd van een pipeline die bouw-, test-, deploy- en integratietest-activiteiten uitvoert, zien we dat de integratietesten in veel gevallen verantwoordelijk is voor het grootste gedeelte van de doorlooptijd. Precies de reden dat het van belang is om zoveel mogelijk ‘laag in de piramide’ te testen. We ontkomen echter vaak niet aan integratie- en acceptatietesten met langere doorlooptijd. We willen tenslotte wel weten dat alle componenten goed samen blijven werken. De gedachte om na elke wijziging te testen is goed, maar toch zijn er best situaties denkbaar waar we met minder dan de volledige testset ook voldoende dekking hadden kunnen halen. Moeten we altijd alle testen draaien? Is het altijd nodig om de regressietest van component X te draaien, of zouden we die kunnen overslaan als onze wijziging geen enkel raakvlak heeft met dat component? Wanneer regressietesten met de hand worden uitgevoerd vragen we de tester toch ook niet om voor elke wijziging alle testgevallen uit te voeren?

In de beweging rond continuous integration & delivery is snellere feedback één van de voorwaarden om succesvol te zijn. Geautomatiseerd testen zóu die feedback moeten versnellen: elke commit wordt direct gebouwd en getest. Eventuele problemen komen dan meteen aan het licht en kunnen direct opgepakt worden. Dat scheelt context switches voor het team en komt daarmee de productiviteit ten goede.

Idealiter zou je de tests die het meest relevant zijn voor jouw change als eerste uitvoeren. Natuurlijk kunnen er altijd onverwachte effecten optreden, maar met de kennis van de applicatie en de testen zijn we vaak best heel goed in staat een ‘educated guess’ te doen over welke tests het meest relevant zijn. Precies dat deden we als testers natuurlijk altijd al, maar met het sneller en vaker releasen en de steeds omvangrijker wordende set van geautomatiseerde testen is dat in de meeste gevallen niet meer haalbaar.

In een tijd waarin elk nieuwsbericht over testen de term “kunstmatige intelligentie” bevat (of het hypetechnisch beter klinkende “artificial intelligence” of “AI”), lijkt een softwarematige oplossing met zelflerende algoritmes voor de hand te liggen. Een neuraal netwerk weet vast wel chocolade te maken van de bergen met testrundata die onze pipeline tot op heden heeft opgeleverd. Waar we allerlei initiatieven in de testwereld zien die ‘AI’ toch vooral gebruiken om een gebruikersinterface te interpreteren (beeldherkenning) en als een soort geautomatiseerde klikrobot door applicaties heen te gaan, zien wij juist in het geautomatiseerd analyseren van historische data en van daaruit optimaliseren van het proces grote verbeterkansen.

Mensen zijn relatief slecht in het nemen van rationele beslissingen en het snel verwerken van grote hoeveelheden ongestructureerde data; juist daar zou kunstmatige intelligentie ons kunnen ondersteunen. Een algoritme zou kunnen helpen inzicht te verschaffen of zelfs keuzes te maken in het proces. Keuzes die niet nieuw zijn, maar door de recente veranderingen in hoe we testen steeds complexer worden en daarnaast steeds sneller en vaker gemaakt moeten worden.

Onze hoop is dat we in deze blogserie duidelijk gemaakt hebben, dat er veel gedaan kan en moet worden voordat zelflerende algoritmes een echte oplossing bieden. We denken zeker niet dat AI de magische oplossing is voor alles: het is een hulpmiddel dat het verschil kan maken tussen “geïnformeerd en snel releasen” en “als de build groen is, zal het wel goed zijn”.

Als we niet langer voor elke wijziging álle testen uitvoeren, maar alleen die testen die ons in korte tijd relevante informatie geven, kunnen we de volgende stap maken in het versnellen van het ontwikkel- test- en leverproces. Mét gebruik van alles wat de laatste technologische ontwikkelingen ons bieden, zónder al teveel te vertrouwen op oplossingen die te mooi lijken om waar te zijn. Het is veel lastiger een algoritme een creatief proces als softwareontwikkeling of -testen te leren, dan het is om een algoritme in te zetten voor het indexeren en classificeren van gegevens. Verder hebben we geleerd, dat optimalisatie niet alleen vanuit AI moet komen. Als we dan toch in modewoorden verzeild moeten raken, klinkt ‘augmented testing’ een stuk realistischer dan testen met ‘AI’.

 www.praegus.nl/solutions