Ik heb al aardig wat testautomatiseringsframeworks opgezet en aangepast. En ik heb altijd al veel aandacht besteed aan begrijpelijkheid en onderhoudbaarheid. Maar sinds ik in de detachering ben gegaan, is dit onderwerp nog meer gaan leven. Je hoort opeens in korte tijd veel meer meningen over wat de ideale code is. Waarbij woorden als SOLID en POM regelmatig langskomen.
Wat SOLID betreft zie ik grotendeels twee kampen: het kamp dat
de best practices altijd toepast en het kamp dat SOLID helemaal niet van
toepassing vindt op testautomatisering. Ik merk dat ik hier tussenin val.
Ik heb deze best practices zelf geprobeerd toe te passen en
ik merkte dat vaak testautomatiseringscode hierdoor in begrijpelijkheid en
onderhoudbaarheid afneemt. De code is te eenvoudig om de best practices consistent
op toe te passen. Hierdoor krijg je extra code lagen, die weinig toevoegen,
maar wel zorgen dat je code nu op 2 of meer plaatsen moet aanpassen, in plaats
van een. Bijvoorbeeld door het toevoegen van interfaces, die slechts een maal in
de code gebruikt worden. Een ander voorbeeld is het opsplitsen van paginacode
in een invoer- en een validatieklasse vanuit de S van SOLID. In de praktijk
betekent dit dat een aanpassing aan één veld je dwingt twee bestanden aan te
passen in plaats van één.
Maar dat betekent niet dat ik vind dat je het zomaar moet
negeren. Het argument “Het is geen productiecode”, dat ik vaak hoor, vind ik
niet terecht. Juist testautomatiseringscode heeft behoefte aan een goede
onderhoudbaarheid. En daar kunnen principes als SOLID wel degelijk aan bijdragen.
Over POM ben ik al veel langer ontevreden. Het is een goede
structuur, zeker. Maar ik heb nooit gesnapt waarom we als testautomatiseerders regelmatig
stoppen bij POM, helpers en datadriven programmeren. Qua data kan ik dit het
snelste duidelijk maken: als een bedrijf besluit om in plaats van ‘Groot-Brittanië’
voortaan ‘United Kingdom’ te gebruiken, op hoeveel plaatsen in je code moet je
dit dan aanpassen? Wie heeft zijn code zo geschreven dat het antwoord hierop ‘1
keer’ is?
Hoe doe ik het dan wel? Ik zal twee recente voorbeelden
geven. Ik heb verschillende datasets gemaakt in mijn code, waarmee je op basis
van een landcode (volgens internationale standaard) data horende bij dat land
op kan halen. De data is op 1 plek vastgelegd. En bij aanpassen wordt
automatisch het invoeren en controleren aangepast, omdat de landcode gelijk
blijft. Daarnaast geeft dit de mogelijkheid om bij het testen eenvoudig
testsets te maken van naar wens 1 land of juist een testset met data van
verschillende landen.
Als extraatje heb ik de landcode zo vastgelegd, dat als ik
er een toevoeg elke dataset niet meer te compileren is. Allemaal geven ze aan
dat er nu een landcode mist. Dit is een variant op het L van SOLID, die je
vraagt om bij een afgeleide van een interface er altijd voor te zorgen dat alle
functies van die interface goed blijven werken. Door mij vertaald: door het
toevoegen van een land moeten alle datasets goed opvraagbaar blijven. Een
ontwikkelaar moet zich niet hoeven afvragen of standaardgebruik leidt tot een
ongewenste uitkomst.
Een ander voorbeeld: een veelvoorkomend testautomatiseringsprobleem
is comboboxen die niet als combobox werken. Je testtool kan in een combobox
niet op de standaard manier een waarde selecteren. Dus haal je trucjes uit met
klikken, invoeren of zoiets om toch maar de waarde geselecteerd te krijgen. En
zo’n combobox komt vaak vaker terug. Deze code heb ik altijd gecentraliseerd,
vaak in een functie. Maar nu heb ik er iets langer over nagedacht. Het
eindresultaat waren twee nieuwe combobox classes. Een voor elk type dat ik in
mijn code tegenkwam. Beiden hebben een selectOption() en een nth(), precies als
Playwright. En beiden zijn gebaseerd op een abstracte klasse, die een eventuele
nieuwe comboboxvariant dwingt dezelfde functies ook te implementeren. Deze
oplossing zorgt dat ik een eventuele aanvulling zo toe kan voegen. Zo had de
eerste versie alleen selectOption. De nth() is later toegevoegd. Als ik later
een derde comboboxvariant tegenkom, voeg ik een nieuwe klasse toe zonder de
bestaande twee aan te raken. Precies wat de O van SOLID beoogt.
In testautomatisering is onderhoudbaarheid belangrijk, net
als begrijpelijkheid. Deze eisen moeten in mijn ogen altijd gaan boven SOLID,
POM of welk design principe dan ook. Dat houdt in dat je geen enkel design
principe pertinent moet afwijzen, omdat het de onderhoudbaarheid wel kan
vergroten. Ook betekent het niet dat je elk design principe blind moet toepassen,
omdat dat de onderhoudbaarheid en begrijpelijkheid kan verkleinen. En evenmin
dat alleen het toepassen van design principes je code onderhoudbaar en begrijpelijk
maakt, zeker niet in testautomatisering.
De vragen die ik mezelf blijf stellen zijn vragen als: wordt
deze code of data op meerdere plekken herhaald? En hoe foutgevoelig is een
aanpassing? Die vragen leiden soms tot een design principe als oplossing, soms
tot een andere oplossing. Maar ik wil deze afweging elke keer opnieuw maken,
alles om onderhoudbaarheid en begrijpelijkheid te blijven verhogen.


.png)


