Onderzoeksverslag Automated testing Qwinsoft B.V. - Coen-Donk/Semester-5-portfolio GitHub Wiki

Introductie

Qwinsoft B.V. is een ICT bedrijf dat een ERP module voor Business Central heeft ontworpen genaamd ‘OVO-vision’. OVO-vision is ontworpen om Business Central beter te kunnen gebruiken in de ei- en pluimvee industrie. OVO-vision zorgt er ook voor dat processen binnen deze industrie gemakkelijker uitbreid baar en te onderhouden zijn. Qwinsoft maakt gebruik van test-driven development om ervoor te zorgen dat hun product een goede kwaliteit heeft. Echter, op dit moment bestaan er maar een paar integratietests. Deze tests maken gebruik van een reeds ontwikkelde database die vroeger bij een klant werd gebruikt. Sindsdien wordt de database gebruikt als testdata, maar deze database bevat veel verouderde gegevens. Het voltooien van de integratietests neemt veel tijd in beslag en omdat het zo lang duurt is het niet mogelijk om al deze tests meer dan één keer op een dag te laten draaien.

Het is mijn taak om deze problemen bij Qwinsoft op te lossen. Om dit zo opitmaal te doen ben ik begonnen met een onderzoek uit te voeren. De hoofdvraag van dit onderzoek is "Op welke manier kan de bestaande testomgeving binnen Qwinsoft worden geoptimaliseerd om te voldoen aan de door Qwinsoft gestelde standaarden en efficiëntie te verbeteren?". Ik verwacht op deze manier een duidelijker beeld te krijgen van wat er gedaan moet worden voordat ik begin aan het praktijk gedeelte.


Onderzoeksvragen en onderzoeksmethode

Hoofdvraag:

Op welke manier kan de bestaande testomgeving binnen Qwinsoft worden geoptimaliseerd om te voldoen aan de door Qwinsoft gestelde standaarden en efficiëntie te verbeteren?

subvragen:

  1. Wat zijn de huidige problemen en bottlenecks binnen de bestaande testomgeving die opgelost moeten worden om efficiëntie te verbeteren en aanpassingen in de software grondiger te testen?

  2. Wat zijn de beste strategieën en technieken voor het ontwerpen en implementeren van unit tests?

  3. Welke strategieën en optimalisaties kunnen gebruikt worden binnen de testomgeving om het test proces te verbeteren?

  4. Hoe kan kan Mock-data en/of kunstmatig gegenereerde gegevens het best geïntegreerd worden in de testomgeving zonder functionele data in gevaar te brengen?

  5. Hoe kan de effectiviteit en efficiëntie van unit tests gemeten worden en welke maatstaven kunnen gebruikt worden om de impact van de verbeteringen van de testomgeving te beoordelen?

  6. Hoe kunnen unit tests effectief (klant)interacties simuleren met het gebruik van Mock-data en/of kunstmatig gegenereerde gegevens?

Om deze vragen te beantwoorden maak ik gebruik van de DOT (Development Oriented Triangulation) framework. De DOT framework bevat meerdere strategieën om een onderzoek uit te voeren, die weer methodes hebben om deze strategieën goed uit te voeren. Deze methodes, en strategieën kunnen gevonden worden bij de deelvragen zelf.


1. Wat zijn de huidige problemen en bottlenecks binnen de bestaande testomgeving die opgelost moeten worden om efficiëntie te verbeteren en aanpassingen in de software grondiger te testen?

Interview

Vragen voor de interview

  1. Welke problemen ondervinden jullie momenteel bij het testen van software-aanpassingen en hoe beïnvloeden deze de efficiëntie?

  2. Hebben jullie al ideeën waarom deze problemen zijn ontstaan?

  3. Hebben jullie al stappen ondernomen om deze problemen aan te pakken? Zo ja, welke?

  4. Zijn er beperkingen in de huidige tools of systemen die jullie gebruiken voor testen?

  5. Hoe communiceren jullie momenteel over testresultaten en problemen binnen de organisatie?

  6. Hoe hebben veranderingen in de manier waarop jullie software ontwikkelen invloed gehad op de manier waarop jullie testen en eventuele problemen die daaruit zijn voortgekomen?

  7. Zijn er specifieke uitdagingen bij het testen van nieuwe functies in vergelijking met het testen van bestaande functionaliteit?

  8. Hoe zouden jullie idealiter willen dat de testomgeving eruitziet om efficiënter te zijn en softwareaanpassingen grondiger te testen?

  9. Ervaart jullie team verschillende problemen bij verschillende versies van de software, met name bij de versies die nog worden onderhouden?

Resultaten van de interviews

Antwoorden:

1 De tests die zijn geschreven duren gewoon te lang. De tests zijn afhankelijk van de database. Soms moet de database zelfs aangepast worden alleen om een test te laten runnen. Omdat de database is aangepast voor test A, werkt test B weer niet en moet het aangepast worden.

2 Onderhoud is niet goed geweest, en vorige versies lieten testing gelimiteerd tot helemaal niet toe. De eerste versie stond testing niet toe, en was nogal moeilijk om mee te werken (Hier werd C/AL gebruikt). Bij versie 8 ongeveer werd er gedeeltelijk testing toegestaan maar deze kon nog niet geautomatiseerd worden. Uiteindelijk moest het project overgezet worden naar AL (Opvolger van C/AL) die wel testing toe liet, maar er was zo veel werk te doen naast het testen dat er geen aandacht aan geschonken is. Nu is die tijd er wel.

3 Er zijn al tests geschreven, maar deze zijn gelimiteerd tot maar één app in specifiek. Er wordt ook gewerkt aan een mock-data generator voor de test data maar deze is nog niet af.

4 De code die nu geschreven is binnen de verschillende applicaties staat het testen qua isolatie niet toe.

5 Tests worden gecommuniceerd via mail, en alleen als een persoon er om vraagt of er verantwoordelijk voor is. Er wordt gewerkt om de transitie naar DevOps te maken

6 Deze vraag werd al beantwoord in vraag 2

7 Nee, de nieuwe functionaliteit is gemakkelijker om te testen. De oude functionaliteit zit nog in elkaar gewoven en het is dus moeilijk tot onmogelijk om een geïsoleerd stukje code te testen omdat deze gewoon niet bereikbaar zijn zonder andere functionaliteit erbij te betrekken.

8 Een testomgeving die toe staat om: Gemakkelijk snelle tests toe te voegen, met een database die klaar staat voor alle tests met accurate test data.

9 Er zijn nog maar twee klanten die oudere versies draaien. Versie 5 wordt niet getest omdat het gewoon niet kan, de versie van 2018 wordt ook niet getest, en alles van 21 en verder wordt wel getest.

Samenvatting resultaten interviews

In de interviews werd al snel duidelijk wat de problemen zijn binnen de bestaande omgeving en hoe deze zijn ontstaan.

Zoals al bekend was, vanwege onderzoek binnen de scope, was dat de tests gewoon te lang duren en afhankelijk zijn van de database. Maar er is ook naar voren gekomen dat de database van tijd tot tijd aangepast wordt om bepaalde tests te kunnen uitvoeren; andere tests breken dan weer en daarvoor moet de database ook weer aangepast worden.

Deze problemen zijn gedeeltelijk ontstaan door achterstallig onderhoud, maar ook omdat eerdere versies van de programmeertaal testing nauwelijks toelieten. Automated testing is iets wat helemaal nieuw is. De oude functionaliteit (geschreven vóór versie 13 en gedeeltelijk afkomstig uit C/AL) is dus sterk verweven met de overige code. Het is niet mogelijk om geïsoleerde stukken code te testen omdat deze afhankelijk zijn van andere code. Voor nieuwe functionaliteit is het wel mogelijk om te testen en wordt dit zo ver mogelijk ook gedaan.

Er zijn nog versies die gebruikt worden door klanten die tests nauwelijks toelaten, en hier worden dus ook geen tests voor geschreven.

Er zijn wel al stappen gezet om deze problemen op te lossen. Er zijn tests geschreven en er wordt gewerkt aan een mock-data generator. Het probleem is dat de tests die zijn geschreven binnen één bepaalde app vallen, en de mock-data generator heeft nog werk nodig voordat deze gebruikt kan worden.

De communicatie over de tests die werken verloopt nu via e-mail, maar er zijn stappen gezet om dit over te zetten naar DevOps.

De gewenste verbetering is dus dat nieuwe tests gemakkelijk toegevoegd kunnen worden, snel zijn en er een database aanwezig is die niet aangepast hoeft te worden om de tests te laten werken.

Literatuur onderzoek

Ik heb niet alleen interviews gehouden, maar ook online onderzoek gedaan om veelvoorkomende problemen en obstakels te vinden in bestaande testomgevingen, samen met mogelijke oplossingen. Mijn doel is om te onderzoeken of deze problemen vaker voorkomen en of deze oplossingen van nut kunnen zijn bij Qwinsoft. Hieronder zijn de resultaten te vinden van veel voorkomende problemen en mogelijke oplossingen daarvoor.

Traag testproces: Het kan lang duren voordat tests zijn voltooid, wat de ontwikkeling en release van software vertraagt.

Oplossing: Automatiseer tests om de doorlooptijd te verkorten en gebruik parallelle testuitvoering om meerdere tests tegelijkertijd uit te voeren.1 3

Onvoldoende testdekking: Niet alle mogelijke scenario's en randgevallen worden getest, waardoor potentiële problemen in productie kunnen optreden.

Oplossing: Identificeer kritieke gebieden van de software en implementeer gerichte testcases om deze beter te dekken. Gebruik code-analyse om niet geteste code op te sporen.

Complexiteit van testdata: Het genereren en beheren van realistische testgegevens kan tijdrovend en moeilijk zijn.

Oplossing: Gebruik gegevensgeneratietools en scripts om testdata efficiënt te genereren en te beheren.

Handmatige testtaken: Handmatige taken in het testproces kunnen fouten en vertragingen veroorzaken.

Oplossing: Automatiseer repetitieve handmatige taken, zoals regressietests, om menselijke fouten te minimaliseren en tijd te besparen.1 3

Gebrek aan testomgevingen: Beperkte testomgevingen voor specifieke configuraties en platforms kunnen beperkingen opleggen aan tests.

Oplossing: Maak gebruik van Cloud gebaseerde testomgevingen die schaalbaarheid bieden en verschillende configuraties ondersteunen. 2 3

Onvoldoende testrapportage: Gebrek aan gedetailleerde rapportage kan het moeilijk maken om problemen te identificeren en op te lossen.

Oplossing: Gebruik testbeheertools om uitgebreide rapporten te genereren en gegevens te analyseren voor betere inzichten. 3

Klantinteracties simuleren in een testomgeving kan complex en tijdrovend zijn, met potentiële inconsistenties tussen ontwikkeling en productieomgevingen.

Oplossing: Docker wordt veel gebruikt om gecontaineriseerd applicaties te maken en te draaien waardoor een geïsoleerde en reproduceerbare omgeving wordt gecreëerd voor het simuleren van klantinteracties. Dit verbetert de betrouwbaarheid en consistentie van tests en maakt het gemakkelijker om verschillende scenario's te testen. 4

Samenvatting interview en literatuur onderzoek.

Kortom, het probleem rondom testing is ontstaan uit een gebrek aan testing mogelijkheden in eerdere versies. Hier wordt aan gewerkt, maar tests die bestaan duren te lang en zijn verbonden met een database die vaak opnieuw ingericht moet worden voor de tests. Uit het literatuur onderzoek heb ik interessante onderwerpen gevonden die gebruikt kunnen worden in de stageopdracht of in de toekomst van Qwinsoft om de testomgeving te verbeteren en versnellen zoals Bijvoorbeeld Docker of parallelle test uitvoering.


2. Wat zijn de beste strategieën en technieken voor het ontwerpen en implementeren van unit tests? 3

De manier van testen in AL verschilt niet veel van de manier van testen in andere talen. AL bevat, net als bijvoorbeeld C#, unit tests, integratietests en regressietests.

Mijn onderzoek heeft aangetoond dat bepaalde technieken worden geadviseerd en andere worden afgeraden, en deze beperken zich niet tot alleen AL.

Best, good and bad practices.

Good practices

Design patterns:

Onderzoek welke methode van testdata de beste is op basis van wat er wordt getest. Elke test heeft testdata nodig, dat is duidelijk. Maar er moet goed worden gekeken naar hoe lang deze data moet blijven bestaan en welke testdata design patterns (testfixture design patterns) moeten worden gebruikt. Er zijn er drie:

Prebuilt fixture: Dit betekent dat de data altijd beschikbaar is en waarschijnlijk in een database staat. Dit is snel, maar als er om welke reden dan ook wijzigingen in de database worden aangebracht, bestaat het risico dat tests worden beïnvloed of zelfs niet meer werken.

Shared fixture: Dit betekent dat de testdata wordt aangemaakt door de tests en ook wordt gebruikt door meerdere tests. Dit kan meer tijd kosten en vereist meer denkwerk over welke data moet worden gegenereerd voor elke test. Dit kan ook een Prebuilt fixture zijn die door meerdere tests wordt gebruikt. Soms wordt zelfs een mock-data generator gebruikt. Deze moeten echter een grote hoeveelheid aan informatie genereren voordat de informatie die belangrijk is voor de test gegenereerd kan worden.

Fresh fixture: Dit betekent dat de data alleen wordt gebruikt voor en door één test.

Er is hier geen "beste" aanpak, maar het wordt afgeraden om alleen maar de Prebuilt fixture te gebruiken. Het nadeel is dat de data die wordt gebruikt om te testen, heel eenvoudig kan veranderen maar wel problemen kan veroorzaken. Shared fixture is in theorie eigenlijk hetzelfde, maar het is wel mogelijk om gemakkelijk nieuwe data te genereren die van hetzelfde type is.

Wens vertalen naar test:

Een andere tip die wordt gegeven bij het schrijven van tests is om een wens te vertalen naar een testplan en dat vervolgens te vertalen naar testontwerp. Dit betekent gebruik te maken van [FEATURE] [SCENARIO] [GIVEN] [WHEN] [THEN]. Hierbij staat [FEATURE] voor de test die wordt gemaakt en wat deze gebruikt om te testen, [SCENARIO] beschrijft wat er gebeurt tijdens het testen, [GIVEN] geeft aan welke data nodig is om deze test goed te laten verlopen, [WHEN] geeft aan waar precies in het pad iets wordt gecontroleerd, en [THEN] beschrijft wat er gebeurt als het goed gaat, of fout, gaat. Door deze informatie in opmerkingen in de code te zetten, is het zelfs voor iemand die geen programmeur is duidelijk wat er precies gebeurt en wat er wordt getest. Hierdoor is het skelet van de test er al, en hoeft er alleen maar code te worden geschreven. Dit is een groot voordeel als er gebruik wordt gemaakt van test-driven development. Tests kunnen al een frame hebben voordat de functionaliteit zelf er is. Via deze methode is het ook makkelijker om tests te schrijven voor iets wat moet falen, wat ook belangrijk is binnen testing.

Test wat echt bestaat

Het is van essentieel belang om ervoor te zorgen dat de testdata en scenario's die worden gebruikt in de tests overeenkomen met hoe een gebruiker de applicatie in de echte wereld zal gebruiken. Door de tests te baseren op realistische situaties en gegevens, kunt je ervoor zorgen dat de tests accuraat zijn, de werking van de applicatie weerspiegelen en potentiële problemen identificeren die gebruikers kunnen tegenkomen.

Hierdoor wordt de validiteit en relevantie van je tests vergroot, en kunnen ze effectiever bijdragen aan het waarborgen van de kwaliteit en betrouwbaarheid van de applicatie. Het zorgt er ook voor dat de tests bruikbare feedback opleveren die ontwikkelaars kunnen helpen om de applicatie te verbeteren op een manier die aansluit bij de behoeften van de gebruikers.

Test niet als het al getest wordt

Het is handig om per test te kijken naar wat je daadwerkelijk wilt testen. Moet er getest worden of iets aan de database toegevoegd wordt? Of een bepaalde error ontstaat? Of misschien of dat er een specifiek resultaat ook echt goed verkregen wordt?

Het is namelijk niet nodig om te testen of iets toegevoegd wordt aan de database. Dat test Microsoft al. Er hoeft ook niet getest worden of iets gevalideerd wordt mits er geen complexe achterliggende functies zijn binnen de validatie. En als deze aanwezig zijn is het de vraag of dat deze daar ook horen en niet misschien in een codeunit. Interacties met de database zijn dus niet echt aantrekkelijk om te testen. Zo ver mogelijk moet er alleen getest worden of de verwachte resultaten ook kloppen van een bepaalde functie.9

Bad practices

Iets wat vaak voorkomt, en sterk wordt afgeraden door veel programmeurs, is het schrijven van te grote tests. Hoewel het belangrijk is om een uitgebreide testdekking te hebben, moet er vermeden worden om tests alleen maar groot te maken met als doel zoveel mogelijk scenario's te dekken. Dit kan namelijk leiden tot verschillende nadelen, zoals:

Complexiteit en Onderhoud

Een van de voornaamste problemen met te grote tests is de complexiteit ervan. Ze zijn vaak moeilijk te begrijpen en te onderhouden. Zowel niet-programmeurs als programmeurs die de testomgeving erven, moeten tijd besteden aan het begrijpen van de test, waar specifiek wordt getest en hoe de test is opgebouwd. Het is belangrijk dat een test snel en eenvoudig te begrijpen is. Zelfs aan de naam van de testfunctie moet men kunnen afleiden "Wat wordt hier getest?" Daarnaast zijn deze grote tests ook nog eens lastig te onderhouden. Stel dat de manier waarop gegevens worden gecreëerd en opgehaald wordt gewijzigd; in dat geval moet een groot deel van de test worden herschreven om deze nieuwe methoden te kunnen gebruiken binnen de test.

Langere Uitvoeringstijd

Het uitvoeren van één grote test kost aanzienlijk meer tijd dan het uitvoeren van meerdere kleinere tests. Het gebruik van meerdere kleine tests heeft ook het voordeel dat als er één test faalt, de overige tests nog steeds succesvol kunnen worden afgerond. Dit maakt het opsporen van fouten gemakkelijker voor programmeurs. Met andere woorden, het identificeren van de exacte locatie van een fout in een grote test kan tijdrovend zijn, terwijl het bij kleinere tests veel efficiënter is.

Onduidelijke Testfocus

Uit grote tests is niet altijd direct duidelijk wat er precies wordt getest. Het kan verwarrend zijn of er meerdere delen worden getest, of dat de grote test grotendeels bestaat uit voorbereiding voor een specifiek gedeelte van de test. Om deze onduidelijkheid te voorkomen en de testresultaten duidelijker te maken is het handiger om meerdere kleinere tests te gebruiken. Dit zorgt ervoor dat elke test een specifieke en duidelijk gedefinieerde focus heeft, waardoor het gemakkelijker wordt om de functionaliteit en het gedrag te begrijpen.

In het kort, het vermijden van te grote tests is van belang om de complexiteit te verminderen, de uitvoeringstijd te verkorten en de helderheid van de testfocus te verbeteren binnen de testing-strategie. Het streven naar kleinere, meer gerichte tests zal het testproces effectiever en efficiënter maken.


3. Welke strategieën en optimalisaties kunnen gebruikt worden binnen de testomgeving om het test proces te verbeteren?

Om deze onderzoeksvraag te beantwoorden, heb ik eerst gekeken naar de reeds bestaande tests binnen OVO-vision. Hierbij viel het me op dat alle tabellen rechtstreeks worden aangeroepen vanuit de tests. In een eerder project heb ik gebruikgemaakt van dependency injection om een losse koppeling te creëren. Ik heb dit nader onderzocht door online te zoeken naar de mogelijkheid om parameters door te geven aan codeunits. Het bleek echter niet mogelijk te zijn, waardoor deze aanpak niet kon worden gebruikt. Bovendien stuitte ik op het feit dat de records statisch zijn; ze moeten direct weten welk record ze moeten gebruiken. Het is dus niet mogelijk om een recordtype aan te maken zonder meteen aan te geven welk specifiek record moet worden gebruikt. Dit maakt het zeer uitdagend om een dynamisch record te creëren. Mijn originele veronderstelling was dus dat dependency injection niet mogelijk was.

Na een blog die recent is gepost blijkt het dat dependency injection wel mogelijk is mist records aan bepaalde standaarden houden. Het is namelijk beter om alle functionaliteit buiten de records te houden en in codeunits te zetten. Deze codeunits managen dan de records. Alle functionaliteit die iets aan het record moet aanpassen roept dan deze codeunit op. Door interfaces te gebruiken op locaties waar records gebruikt worden, is het mogelijk om een abstracte connectie te leggen tussen de database en de functionele code. Dit betekent dat er geen directe link wordt gelegd. Door het gebruiken van interfaces is het dus mogelijk om pas bij het gebruiken van de codeunit te bepalen welke codeunit er gebruikt moet worden. Voor testen is het handig om een codeunit aan te roepen die niks aan de database aanpast om zo snelle tests aan te houden en dat er weinig opzet nodig is wat ook tijd bespaart bij het ontwerpen en het daadwerkelijk uitvoeren van de tests. 9

Interview

Tijdens het bekijken van de code merkte ik ook op dat er weinig asserties worden gebruikt bij de integratietests. Er zijn grote testmethoden geschreven, maar het bleef onduidelijk wat er eigenlijk werd getest. Om hier meer duidelijkheid over te krijgen, heb ik Bart (de bedrijfsmentor) gevraagd: "Wat wordt er eigenlijk getest?" Hieruit bleek dat er alleen wordt gecontroleerd of er geen fouten optreden in de vorm van een error. Er wordt dus niet echt getest met behulp van asserties; met andere woorden, er wordt eigenlijk niets getest. Daarnaast is het ook lastig om de code goed te modulariseren omdat er geen duidelijke molariteit aanwezig is.

Hieruit kan ik concluderen dat er meer nadruk moet worden gelegd op modulariteit. Dit betekent dat de code niet afhankelijk moet zijn van andere code, tenzij dit absoluut noodzakelijk is. Zelfs in dergelijke gevallen moet er zorgvuldig worden nagedacht over hoe dit is opgezet. Bovendien moet er meer aandacht worden besteed aan wat er precies wordt getest. Welke aspecten worden aangepast tijdens de test? Wordt er gecontroleerd of deze wijzigingen correct worden doorgevoerd? Wordt er gekeken naar mogelijke informatie die tijdens de test verloren gaat? Er moet ook worden voorkomen dat dezelfde code meerdere keren wordt getest. Als een functie zowel in test A als in test B wordt aangeroepen, moet er worden overwogen waarom dit het geval is.

Als laatste moet er duidelijk zijn welk resultaat er bij de integratietest verkregen moet worden. Welke onderdelen worden er getest? Als een integratie van begin tot eind helemaal door test is het dan niet een end-to-end test in plaats van een integratietest?


4. Hoe kan kan mock-data en/of kunstmatig gegenereerde gegevens het best geïntegreerd worden in de testomgeving zonder functionele data in gevaar te brengen?

Interview en Component test

Om deze deelvraag te beantwoorden heb ik aan de product owner gevraagd of er al een mock-data generator bestaat en hoe deze geïmplementeerd is. Het bleek dat er een basis van een flock data generator aanwezig is omdat deze anders niet getest kan worden, maar daarnaast is er nog niks. Ik ben daarom van plan om zelf al een begin te maken van een mock-data generator om te kijken hoe deze het beste geïmplementeerd kan worden. Ik verwacht dat deze het beste ontworpen kan worden door de mock-data in de bestaande records op te slaan, tests er op uit te voeren, en daarna weer te verwijderen. Door gebruik te maken van een hard gecodeerd ID, is het gemakkelijk om de originele test data terug te vinden en te verwijderen zodat het niet in de development of production omgeving achterblijft. Door het ID zo uniek te maken dat het apart is van hoe een ID er normaal uit te zien, is het ook gemakkelijk te differentiëren van andere items.

Na dit toe gepast te hebben binnen de applicatie klopte het dat het mogelijk is om een data generator te ontwerpen en integreren die de data opslaat in bestaande records. Ik heb de data generator op zo'n manier ontworpen dat het ook mogelijk is om handmatig data toe te voegen in plaats van automatisch te genereren.

De functionele data wordt niet in gevaar gebracht omdat AL al een component bevat om data die aangepast wordt tijdens de test te resetten. Dit gebeurt door "isolation" toe te passen. Er zijn verschillende opties binnen de isolation. Alle data kan gereset worden na iedere test run, na het draaien van alle tests binnen één bestand en de reset kan gebeuren na iedere test. Dit geeft veel vrijheid om te bepalen welke data er gebruikt wordt binnen de test, hoe dit gebruikt wordt en wat er mee gebeurt na een test. Mijn originele aanname van een hard gecodeerd ID gebruiken is dus fout.


5. Hoe kan de effectiviteit en efficiëntie van unit tests gemeten worden en welke maatstaven kunnen gebruikt worden om de

impact van de verbeteringen van de testomgeving te beoordelen?

Een bekende manier om de effectiviteit van unit tests te meten is code coverage. Echter, code coverage is niet altijd de beste maatstaf om te gebruiken. Stel je hebt een grote applicatie, is het dan nog haalbaar om te streven naar een hoge code coverage? Zo niet, hoe kun je dan nog steeds de effectiviteit van je unit tests meten?

Er zijn andere opties om te beoordelen of je unit tests effectief en efficiënt zijn.

Effectiviteit

Voor effectiviteit is het een optie om te kijken naar nieuwe bugs. Als er een nieuwe bug uit je tests naar voren komt, betekent dit dat deze bug mogelijk nog een keer kan optreden. Dit kun je voorkomen door een test te schrijven die controleert of deze bug nog steeds voorkomt. Als dat het geval is dan moet de test mislukken. Nadat je dit hebt gedaan kun je de bug repareren. Dus je moet eigenlijk twee dingen oplossen: je moet ervoor zorgen dat je de bug kunt detecteren en dat de bug niet meer bestaat.

Er bestaat een methode die specifiek bugs creëert om te testen of de tests deze bugs kunnen opsporen. Deze vorm van testen staat bekend als mutation testing. Bij mutation testing wordt bij elke uitvoering een mutatie, een wijziging in de code, toegepast en vervolgens worden de tests uitgevoerd. Op deze manier kun je het beste achterhalen of je tests alles opmerken. Een probleem in de AL-omgeving is echter, voor zover ik weet, dat het niet mogelijk is om mutation testing toe te passen en de vraag is of de inspanning die hiervoor nodig is opweegt tegen wat de tests opleveren. 5 6

Een andere optie is het gebruik van de test case effectiveness metric. Dit is geen directe aanpassing aan de tests of een andere manier van test schrijven, maar het berekent hoeveel procent van de bugs die je in je project tegenkomt ook daadwerkelijk zijn gedetecteerd.

Dit bereken je door: Bugs gevonden / (bugs gevonden + bugs niet gevonden) * 100%. Hierbij is 100% perfect en 0% zeer slecht. Op deze manier krijg je een beter inzicht in hoe effectief je testomgeving is. 7 Gevonden bugs zijn bugs die automatisch door het systeem zijn gevonden. Bugs niet gevonden zijn bugs die niet gevonden zijn door het systeem en dus handmatig zijn gevonden of gevonden zijn in de productie versie.

Efficiëntie meten in de code.

Voor efficiëntie is het vrij eenvoudig. Meet hoe lang je tests duren. Je kunt de volgende formule gebruiken om te helpen:

Testefficiëntie (in procenten) = aantal tests / totale tijd (In seconden) * 100%. Hierbij staat 100% voor zeer efficiënt en 0% voor zeer traag. Natuurlijk kan het variëren van test tot test of een test al dan niet efficiënt is. Maar als de score laag is en er zijn veel tests, is de vraag of al deze tests daadwerkelijk samen moeten worden uitgevoerd. Stel dat je een lage score hebt en een paar tests die lang duren. Het is dan de vraag of deze tests kunnen worden opgesplitst of elders efficiënter kunnen worden uitgevoerd, bijvoorbeeld door middel van parallel testen.8

Conclusie

In het kort zijn dit de resultaten van het onderzoek van deze deelvraag: er zijn twee methoden die directe cijfers opleveren over efficiëntie en effectiviteit. In beide gevallen moet kritisch worden gekeken naar de tests als de score laag is. En als de score laag is, is het de vraag wat er kan worden aangepast aan de tests om een hogere score te behalen. Wat betreft effectiviteit zijn er zeker opties om het effectiever te maken, maar het is opnieuw de vraag of dit de moeite waard is.


6. Hoe kunnen unit tests effectief (klant)interacties simuleren met het gebruik van mock-data en/of kunstmatig gegenereerde gegevens?

Uit onderzoek naar wat er gebruikt wordt in veel ontwikkelomgevingen blijkt dat er veel gebruik gemaakt wordt van mock-data om de input van klantinteracties na te bootsen. Dit houdt in dat nepgegevens worden gecreëerd die de interactie van een klant met de toepassing nabootsen. Dependency injection wordt veel gebruikt om ervoor te zorgen dat de testomgeving een 'nep' database gebruikt. Interfaces, die een vorm van loose coppeling mogelijk maken, Kunnen deze loose coupling creëren die nodig is om een nepgegevens aan te maken voor het schrijven van Unit tests. Deze interfaces worden echter veel minder gebruikt dan dat ze gebruikt zouden moeten binnen OVO-vision en veel functionaliteit is geschreven binnen de tables. Hierdoor is het moeilijk om achteraf interfaces te introduceren zonder veel aanpassingen te moeten brengen. 9 Het is niet onmogelijk om interfaces te gebruiken maar het gaat tijd kosten om dit toe te voegen binnen OVO-vision en het vereist veel aanpassingen. Dit zorgt ervoor dat tests geprogrammeerd kunnen worden die geen communicatie vereisen met de database en niet geforceerd zijn om een patroon te volgen voordat de gegevens van een test opgezet kunnen worden. Het is dus mogelijk om echte unit tests te schrijven.

Voor de integratietests is er echter een andere gedachte. Integratietests moeten veel opzet gebruiken of aanmaken om hun tests te kunnen runnen. Binnen deze opzet vallen ook basis instellingen of gegevens die niet aanwezig zijn in een standaard database. Het is daarom een betere aanpak bij de integratietests om een gegevensgenerator te ontwikkelen. Deze generator kan automatisch en realistische testgegevens genereren en deze rechtstreeks aan de database toevoegen. Dit stelt ontwikkelaars in staat om klantinteracties nauwkeurig na te bootsen zonder de beperkingen van mock-data, hoeft de test omgeving niet gebruik te maken van een specifieke omgeving, en kunnen eventuele wijzigingen die tijdens de tests worden aangebracht eenvoudig worden teruggedraaid, zodat de functionele gegevens van de toepassing niet in gevaar komen. Door de generator op een dynamische manier te ontwerpen is het ook mogelijk om specifieke gegevens te genereren voor specifieke tests als dit vereist is. Hoe (klant)interacties gesimuleerd kunnen worden via deze generator is simpelweg afhankelijk van welke interactie gevraagd wordt en waar dit moet gebeuren. De gegevens kunnen dan gegenereerd worden op een random manier of op een voorgeprogrammeerde manier.

Conclusie

Kort Samengevat: Voor unit tests is het mogelijk en handig om dependency injection te gebruiken, mits er interfacing aanwezig is binnen OVO-vision. Zolang dit niet aanweezig is zijn Unit tests alsnog te maken maar is er meer opzet data vereist wat lang kan duren en zijn het niet echt unit tests maar integratietests. Voor zowel oude als nieuwe integratietests is het handiger om een data generator te maken die de data voor de integratietests opzet. Hieronder valt ook basis data zoals bijvoorbeeld instellingen die nodig zijn voor bepaalde onderdelen van de integratietests.


Conclusie

Om een goede conclusie te vormen van het onderzoek zal ik hieronder mijn vragen opnieuw beantwoorden

  1. Testen duren te lang, databases moeten vaker aangepast worden, er zijn weinig tot geen unit tests en code is sterk verweven. Er zijn opties om testen te verbeteren. Waaronder: Parallel testing, Docker en gegevensgeneratoren

  2. Overweeg welke vorm van testfixture design pattern het beste gebruikt kan worden per test. Wanneer moet er iets random gegenereerd worden en wanner moet de informatie altijd hetzelfde zijn? Vertaal een wens naar een test. Schrijf per test de [FEATURE] Die getest wordt, welk [SCENARIO] er is, welke informatie nodig is [GIVEN], [WHEN] het gebeurt en wat er gebeurt [THEN]. Hou de tests duidelijk en niet complex of te groot. Dat alle tests samen in een bestand kunnen betekent niet dat ze ook samen moeten.

  3. Maak de bestaande integratietests modulair, maak duidelijker wat er nu getest wordt in de reeds bestaande tests en onderzoek wat voor test er uitgevoerd wordt. Unit, integratie of end-to-end test.

  4. Business Central bevat al functionaliteit binnen tests om functionele data niet in gevaar te brengen, en maak gebruik van een generator die niet afhankelijk is van hardgecodeerde informatie.

  5. Maak gebruik van de effectiviteit en efficiëntie formule om te meten hoe effectief en efficiënt de test omgeving of specifieke onderdelen van de test omgeving zijn.

  6. Dependency injection is mogelijk binnen business central maar dit kost tijd. Er kan gebruik gemaakt worden van een data generator om de opzet van de integratietests te versnellen, maar uiteindelijk moet de overstap naar dependency injection gemaakt worden.

Als conclusie adviseer ik dat Qwinsoft gebruik maakt van de resultaten van dit onderzoek en zo mogelijk de integratietests aan past of de manier van testen aanpast. De integratietests moeten aangepast worden om ze sneller te maken en een datagenerator is daarvoor nodig. Dit kan ook door gebruik te maken van dependency injection. De manier van tests uitvoeren kan ook bevorderen bij de snelheid van de tests door bijvoorbeeld gebruik te maken van parallel testing waar mogelijk. Door te meten hoe snel of effectief te tests zijn wordt het duidelijker waar de problemen binnen de test omgeving zijn.


Bronnen

[1] 7 Augustus, 2023, Parallel Testing, geraadpleegd op 11/09/2023 van https://support.smartbear.com/testcomplete/docs/testing-approaches/parallel-testing.html#:~:text=Parallel%20testing%20is%20testing%20multiple,or%20features%20of%20an%20application.

[2] Wesley Chai, Februari 2021, Cloud Testing, geraadpleegd op 11/09/2023 van https://www.techtarget.com/searchstorage/definition/cloud-testing

[3] Van Vugt, Luc, Automated Testing in Microsoft Dynamics 365 Business Central, Packt, 10 December 2021, geraadpleegd op 11/09/2023

[4] https://docs.docker.com/get-started/overview/

[5] Ajay, Aug 23, 2019, Measuring Unit Testing Effectiveness, geraadpleegd pleegt op 22/09/2023 https://medium.com/mathematicallygifted/my-view-on-measuring-unit-testing-effectiveness-cf2d3db7d481

[6] Unknown, Unknown, Mutation Testing, geraadpleegd op 22/09/2023 van https://en.wikipedia.org/wiki/Mutation_testing

[7] Yamini Priya, June 15, 2023, Test Efficiency, geraadpleegd op 22/09/2023 van https://testsigma.com/blog/test-efficiency/

[8] Unknown, June 30, 2023, Efficiency Testing, geraadpleegd op 22/09/2023 van https://www.softwaretestinghelp.com/efficiency-testing/

[9] Vjekko, Nov 2, 2023, Directions EMEA 2023 demo – decoupling base app, geraadpleegd op 6/11/2023 van https://vjeko.com/2023/11/02/directions-emea-2023-demo-decoupling-base-app/#comments

⚠️ **GitHub.com Fallback** ⚠️