Advertentie
Softwareontwikkeling
Visuele regressietests in de CI-workflow: zo komt de lay-out in de pipeline
Unit-tests toetsen logica, maar niemand toetst de lay-out: hoe visuele regressietests in een CI-pipeline passen, wanneer ze zouden moeten draaien en waar hun grenzen liggen — uitgelegd aan de hand van screenshotdienst ScanU.

Moderne ontwikkelteams vertrouwen op hun continuous-integration-pipeline: elke push start een build, de unit-tests lopen door, de linter controleert de stijl, en pas als alles groen is verhuist de code naar de hoofdtak. Die beveiliging heeft echter een blinde vlek. Of de website er na de wijziging nog uitziet zoals hij eruit hoort te zien, controleert de klassieke pipeline niet. Een CSS-refactoring kan alle tests doorstaan en toch het contactformulier achter de footer duwen.
De oorzaak ligt in de aard van de gangbare testsoorten. Unit-tests toetsen functies aan verwachte retourwaarden, integratietests het samenspel van componenten, end-to-end-tests klikken zich door processen heen en controleren of gedefinieerde elementen aanwezig zijn. Geen van deze benaderingen ziet de pagina zoals een mens haar ziet: als gerenderd beeld. Een knop kan technisch aanwezig, klikbaar en correct gelabeld zijn — en toch in wit op wit onzichtbaar blijven.
Hoe zulke breuken ontstaan, laat een blik op de dagelijkse praktijk zien: een dependency-update trekt een nieuwe versie van het CSS-framework mee, die een paar standaardafstanden anders zet. Een refactoring hernoemt een utility-klasse die op een vergeten plek nog gebruikt wordt. Een lettertypebestand wordt vervangen en valt minimaal breder uit, waarop een navigatie afbreekt. Niets daarvan werpt een fout op, niets daarvan sneuvelt in de build — en elk van deze wijzigingen ziet er in de codeweergave volkomen onschuldig uit.
Visuele regressietests dichten dat gat met een eenvoudig principe. Van vastgelegde pagina's wordt een referentietoestand bewaard, de zogeheten baseline. Na elke wijziging ontstaan nieuwe screenshots, die een werktuig met die baseline vergelijkt. Wijkt het actuele beeld af, dan slaat de controle aan, en een mens beslist: gewenste wijziging of regressie?
De interessante vraag voor de praktijk is daarbij minder het of dan het waar: op welke plek in de CI-workflow horen zulke controles thuis? Anders dan unit-tests, die rechtstreeks op de broncode werken, hebben visuele tests een gerenderde, via het netwerk bereikbare pagina nodig. Dat schuift ze naar het eind van de keten — daarheen waar al een werkende versie van de applicatie bestaat.
Diensten als ScanU werken daarom URL-gebaseerd: je geeft het adres van een bereikbare pagina door, en de dienst maakt in ongeveer 30 seconden screenshots over meerdere browsers en apparaatklassen heen — Chrome, Firefox en Safari, telkens in mobiele, tablet- en desktopweergave. Hoe ScanU zo'n controleronde stap voor stap opbouwt, beschrijft de aanbieder zelf; het grondpatroon geldt echter voor elk URL-gebaseerd werktuig: zonder een omgeving die de nieuwe stand uitlevert, valt er niets te fotograferen.
Het elegantst past dat in workflows met zogeheten deploy previews. Veel hostingplatforms bouwen voor elke pull request automatisch een eigen voorbeeldomgeving met een eigen adres. Precies dat adres is het natuurlijke aanknopingspunt voor de visuele vergelijking: de preview toont de stand na de wijziging, de baseline de vrijgegeven stand daarvoor. Meldt de vergelijking een afwijking, dan zien alle betrokkenen het probleem voordat de code de hoofdtak bereikt — en de discussie vindt plaats waar ze thuishoort: in de review.
Concreet ziet de volgorde in zo'n pipeline er dan zo uit: eerst bouwt de CI de nieuwe stand, dan publiceert het hostingplatform de voorbeeldomgeving en meldt het adres daarvan terug, en pas daarna mag de visuele controlestap starten. Precies op dat punt gaat er in de praktijk het vaakst iets mis. Wie de screenshotronde aanstoot voordat de preview volledig is uitgeleverd, fotografeert een half geladen pagina of een foutmelding — en vergelijkt die vervolgens in alle ernst met de baseline. Een korte bereikbaarheidscontrole van het previewadres vóór de eigenlijke ronde bespaart dat soort verwarring. Is het rapport klaar, dan hoort de link ervan bij de pull request, als commentaar of in de beschrijvende tekst, zodat de beoordeling plaatsvindt waar toch al over de wijziging wordt gesproken. Deelbare rapporten, zoals ScanU ze genereert, zijn precies voor die handeling bedoeld: het rapport is de gemeenschappelijke discussiebasis, niet een gereedschapsvenster dat maar één persoon geopend heeft.
Teams zonder previewomgevingen wijken uit naar de stagingomgeving en controleren na de merge, maar vóór de productie-deployment. Dat is de op één na beste oplossing: regressies vallen later op, wanneer meerdere wijzigingen zich al vermengd hebben, en de zoektocht naar de veroorzaker wordt moeizamer. Een extra regelmatige ronde tegen de productiesite vangt bovendien veranderingen op die helemaal niet uit de eigen code stammen — bijvoorbeeld wanneer een extern ingebonden script of een CMS-update het uiterlijk verschuift.
De technische inbedding is daarbij het kleinere deel van het werk. Het grotere is een proceskwestie: het onderhoud van de baseline. Een baseline is geen technisch artefact, maar een afspraak — zo hoort de pagina eruit te zien. Wie een gewenste designwijziging uitrolt, moet de baseline daarna bewust bijwerken, anders meldt elke volgende ronde diezelfde verwachte afwijking. Wie hem daarentegen reflexmatig na elk alarm bijwerkt, verklaart vroeg of laat een echte fout tot gewenste toestand.
Beproefd is het om baseline-actualisaties als codewijzigingen te behandelen: iemand kijkt ernaar, iemand geeft vrij, en die vrijgave is navolgbaar. ScanU zet daarvoor baseline en actuele stand naast elkaar en genereert deelbare rapporten. Dat klinkt onspectaculair, maar het verandert de samenwerking: ook collega's uit design of productmanagement kunnen over een afwijking oordelen zonder een ontwikkelomgeving te starten — en de beslissing „gewenst of kapot" is vaak geen puur technische.
Verder zou een team moeten uitmaken wie eigenaar van de controle is. Visuele tests die aan niemand zijn toegewezen, raken gegarandeerd verweesd: rapporten stapelen zich op, niemand voelt zich verantwoordelijk, na een maand negeert iedereen ze. Beproefd is dezelfde regel als bij kapotte builds — wie de wijziging heeft veroorzaakt, ontfermt zich over de gemelde afwijking, en een roulerende dienst houdt de periodieke ronden in de gaten.
Bij de planning hoort ook de vraag hoe vaak er gecontroleerd wordt. Niet elke commit heeft de volledige ronde over alle pagina's en apparaatcombinaties nodig. Afrekenmodellen zoals het creditsysteem van ScanU maken die afweging concreet: het gratis niveau omvat 500 credits per maand voor één project, de betaalde niveaus lopen van 3000 credits voor 19 euro tot 50000 credits voor 49 euro per maand. Wie elke pipeline-uitvoering door alle apparaatprofielen jaagt, verbruikt zijn contingent snel — en produceert er meteen meer rapporten bij dan het team überhaupt kan bekijken.
Zinniger is een gelaagde aanpak. Pull requests met wijzigingen aan styles of templates krijgen de vergelijking over de belangrijkste pagina's; een dagelijkse of wekelijkse ronde dekt de bredere set over alle browsers af; vóór een release draait alles één keer. Zo blijft de signaal-ruisverhouding gezond, en heeft elk alarm een reële kans om serieus te worden genomen. Een visuele test waarvan het team de meldingen routineus wegklikt, is waardelozer dan helemaal geen test.
Bij de keuze van de pagina's geldt: templates in plaats van instanties. Een webshop hoeft niet 2000 productpagina's te fotograferen, maar één representatieve pagina per lay-outtype. Homepage, één product- of artikelpagina per sjabloon, winkelwagen, formulieren en de foutpagina dekken bij de meeste projecten het grootste deel van het risico af. Pagina's met hoge zakelijke waarde — bijvoorbeeld het betaalproces — verdienen daarbij meer apparaatcombinaties dan het Impressum, de wettelijk verplichte aanbiedersinformatie op Duitse sites.
Dezelfde afweging geldt voor apparaatprofielen. De prijsniveaus van ScanU verschillen ook in het aantal beschikbare apparaten — van tien in het gratis niveau tot 98 in het grootste. Dat verleidt tot volledigheid, maar meer profielen betekenen meer beelden, meer credits en meer beoordelingswerk. Praktischer is de oriëntatie op de eigen bezoekcijfers: de drie, vier apparaatcombinaties waarlangs het gros van de bezoekers daadwerkelijk binnenkomt, plus telkens één vertegenwoordiger van de randformaten — heel smal, heel breed.
Eén hindernis verdient bijzondere aandacht: dynamische inhoud. Roterende teasers, gepersonaliseerde aanbevelingen, cookiebanners, ingevoegde kloktijden of advertenties veranderen het beeld bij elke oproep — geheel zonder regressie. Zulke valse alarmen zijn de vaakst voorkomende reden waarom teams visuele tests na enkele weken weer uitschakelen. De tegenmiddelen zijn bekend: testomgevingen met vaste voorbeelddata, uitgeschakelde animaties, consistente uitgangstoestanden. Helemaal verdwijnen valse alarmen nooit, maar ze laten zich tot een draaglijk niveau terugdringen.
Bij eerlijkheid hoort ook het benoemen van de grenzen. Een geslaagde screenshotvergelijking zegt niets over de vraag of het formulier daadwerkelijk gegevens verstuurt, de zoekfunctie treffers oplevert of het betaalproces doorloopt. Visuele tests toetsen weergave, geen functie — ze vullen unit- en end-to-end-tests aan, ze vervangen die niet. Wie zijn functionele testdekking ten gunste van screenshots terugschroeft, ruilt het ene risico in voor het andere.
Ook de zeggingskracht hangt van de omgeving af. URL-gebaseerde controles fotograferen wat het opgegeven adres uitlevert. Verschilt de stagingomgeving duidelijk van de productie — andere gegevens, andere feature flags, ontbrekende scripts van derden —, dan controleert men een vertekend beeld. En delen achter een login vragen om aparte overwegingen voordat een externe dienst ze überhaupt kan vastleggen.
Om de controle in het team verankerd te houden, helpt een nuchtere omgang met de resultaten ervan. Het loont om af en toe de balans op te maken: hoeveel gemelde afwijkingen waren echte regressies, hoeveel valse alarmen, hoeveel gewenste wijzigingen? Kantelt de verhouding richting vals alarm, dan is dat geen reden om uit te schakelen, maar een werkopdracht aan de testomgeving. En vindt de vergelijking maandenlang eenvoudigweg niets, dan mag een team de controlefrequentie ook verlagen — testdiscipline betekent niet maximale uitbouw, maar passendheid.
Een onderschat aspect is de historie. Valt een regressie pas na dagen op, dan helpt de blik terug: wanneer zag de pagina er voor het laatst correct uit, welke periode komt als veroorzaker in aanmerking? ScanU bewaart verlopen afhankelijk van het prijsniveau tussen drie en 90 dagen. Ook dat hoort in de planning thuis — een historie van drie dagen is over een lang weekend snel opgebruikt, terwijl 90 dagen ook de vraag beantwoorden of een probleem al vóór de laatste kwartaalrelease bestond.
Voor de start pleit veel voor een klein begin: één project, een handvol pagina's, een wekelijkse ronde plus één ronde per release. Groeit het vertrouwen in de alarmen, dan volgt de nauwere verweving met de pipeline. Details over de werkwijze staan in de documentatie van ScanU; een overzicht in het Nederlands biedt de Nederlandstalige productpagina van de aanbieder.
Blijft de principiële vraag: loont de moeite? Het antwoord hangt minder van de teamgrootte af dan van de wijzigingsfrequentie. Wie wekelijks deployt, produceert wekelijks gelegenheden voor stille lay-outbreuken — en ontdekt ze zonder systematische controle pas wanneer gebruikers ze allang gezien hebben. De CI-pipeline heeft de ontwikkelpraktijk geleerd om logicafouten vóór publicatie op te vangen. Er zijn weinig redenen om de zichtbaarste laag van een website diezelfde bescherming te onthouden.