Voorbereiding: Opstellen van het test-vangnet

Refactoring is een beheersbare methode om de structuur van een software systeem te wijzigen, op zö'n manier dat het gedrag van het software systeem zelf niet wijzigt. Uiteraard weet je dit nooit 100% zeker. Daarom is het belangrijk dat je zorgt voor een vangnet. Testen helpen je daarbij.

Opstellen van de juiste mind-set

Als garantie dat we het gedrag van het systeem niet gewijzigd hebben, voeren we regressie-testen uit. Elke refactoring-stap voeren we uit als een mini-cyclus, bestaande uit 3 delen:

  1. Regression-testing: Verzeker jezelf ervan dat het systeem correct werkt. Indien aan deze voorwaarde niet voldaan is, is het vaak onmogelijk om na te gaan of je tijdens de volgende stap geen fouten introduceert.
  2. Refactoring
  3. Regression-testing: Verzeker jezelf ervan dat het systeem nog steeds correct werkt.

Vaak heb je niet de luxe om te rekenen op een optimale test-suite. Dit betekent dat je, zo goed en zo kwaad het kan, je een test-vangnet in werking moet stellen. Alhoewel niemand nog overtuigd moet worden van het nut van unit-tests, zullen we in deze sessie de meest primitieve vorm van tests gebruiken als vangnet: input-output testen.

Input-output testen vergelijken de resultaten van het uitvoeren van een scenario met een zogenaamde baseline. Deze baseline is kritiek, gezien ze als referentie gebruikt wordt. Daarom is het erg belangrijk dat deze baseline een weergave vormt van een correcte uitvoering van een scenario.

Jammer is dat je door het vergelijken van de resultaten van een scenario-uitvoering met de baseline louter fouten kan opmerken. Input-output testen geven typisch erg weinig input om de juiste oorzaak van de fout te traceren, vermits je slechts het eindresultaat van de fout evalueert. Daarom is het ontzettend belangrijk dat je refactoring-stappen (stap 2 in bovenstaande mini-cyclus) zo klein mogelijk maakt. In het ideale geval zijn deze stappen de kleinst mogelijke veranderingen die je systeem opnieuw in een compileerbare staat brengt. Indien je dit niet doet, zal je meerdere stappen tegelijk uitvoeren. Aangezien input-output testen je niet kunnen helpen bij het debuggen, zal je niet zeker kunnen weten welke stap de fout introduceerde. Indien je slechts één stap uitvoert weet je dit wel, en kan je eenvoudigweg die éne stap ongedaan maken om de fout te verwijderen :)

Daarom introduceren we volgende gouden regels:

  1. Hou je aan de mini-cyclus die bovenaan deze pagina beschreven wordt.
  2. Voer in elke cyclus slechts de kleinst mogelijke één refactoring-operatie uit die je systeem terug compileerbaar maakt.
  3. Maak, indien je een fout opmerkt, je laatste stap ongedaan, en begin opnieuw.

Ervaring heeft getoond dat het schenden van bovenstaande regels volstaat om in een toestand te geraken waarin het hopeloos veel energie kost om opnieuw tot een correct systeem te te komen. Neem daarom deze gouden regels ter harte.

Doorheen de oefeningen zullen we je herinneren aan het consistent toepassen van bovenstaande mini-cyclus. Hiervoor gebruiken we volgende layout:

Het uitvoeren van testen markeren we in deze stijl.

Uitvoeren van de testen

De LAN-simulatie heeft slechts één main functie. Deze is gelocalizeerd in source-file LanSimulation.cpp. De main-functie biedt twee mogelijkheden:

  1. Simulatie: Hiervoor moet je bij het uitvoeren van de gegenereerde executable argument s meegeven.
  2. Test: Hiervoor gebruik je argument t. Om de tests te draaien moet je dus de main-functie oproepen met het argument t.

Compileren van de LAN-simulatie

Aangezien het project lanSimulationCDT een zogenaamd Managed Make C++ project is, neemt Eclipse de makefile voor zijn rekening.

Uitvoeren van de testen

Om een executable te maken, moet je eerst het lanSimulationCDT project build-en. Na een succesvolle build zal je merken dat er een extra foldertje Binaries verschijnt:


Gegenereerde binaries

Eclipse heeft op dit moment de executable lanSimulation.exe gegenereerd. Om deze uit te voeren, moet je eerst een nieuwe run target maken. Klik daarom eerst op het Run knopje, en klik daarna op Run..., zoals hieronder weergegeven:

Je krijgt nu een venster te zien, waarin je een nieuwe C++ Local Application target kan aangeven:

Klik op C++ Local Application en dan op het New launch configuration knopje, dat aangegeven wordt als een icoon van een bestand met een geel plus-teken erop. Eclipse zal zelf de naam van het project invullen (LanSimulationCDT), maar je moet nog wel zelf aangeven welke binary moet uitgevoerd worden. Klik daarom op Search Project... en selecteer de executable lanSimulation.exe, zoals hieronder weergegeven:

Vanaf dit ogenblik weet Eclipse dat hij lanSimulation.exe moet uitvoeren, wat neerkomt op het oproepen van de main-functie in source-file LanSimulation.cpp. Zoals we eerder reeds zagen moeten we nog een argument meegeven om de testen te draaien. Vul daarom in tab-blad Arguments karakter t in als argument, zoals hieronder weergegeven:

Klik nu op Apply en vervolgens op Run. Hierdoor zal je voor de eerste keer de testen uitvoeren.

Je zal opmerken dat bij het uitvoeren van de tests voor de eerste keer, je volgende fout zal krijgen:

Running LANtests...
	testBasicPacket...[ok]
	testBasicNode...[ok]
	testDefaultNetworkToString...[ok]
	testWorkstationPrintsDocument...[ok]
	testBroadcast...[ok]
	testOutput...
File "expectedOutput.txt" does not exist.
Assertion failed: compareFiles(generateOutputFName, expectedOutputFName), file ../lanSimulation/tests/LANTests.h, line 182

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

De assertie die hier gefaald is is devolgende:


Assert gebruikt in input-output test

Deze assert vergelijkt een gegenerereerd bestand (de huidige output) met een ander bestand (de expected output). De gegenereerde file heeft de naam useOutput.txt. Dat bestand mag je hernoemen naar expectedOutput.txt. Wanneer je dan opnieuw de testen uitvoert, zal je merken dat deze slagen.

Als voorbereiding van deze hebben we ervoor gezorgd dat de huidige implementatie correct is. Je mag daarom bestand useOutput.txt hernoemen naar expectedOutput.txt. Voer nu voor een tweede maal de testen uit, door op het groene Run-knopje te klikken (kijk eventueel of de juiste run-target wel wordt uitgevoerd). Je al nu merken dat je geen fouten meer krijgt.

Op dit ogenblik ben je aan de start van de befaamde Test-Refactor-Run mini-cyclus gekomen, en kunnen we starten met de eerste refactoring-oefeningen!