Mocking
We bouwen verder op het voorbeeld dat we hebben gezien in het vorige hoofdstuk
1. Voeg een nieuwe interface toe met de naam IDie
2. We werken deze interface als volgt uit:
public interface IDie
{
int Roll();
}
3. We passen nu onze bestaande Die klasse aan en implementeren op deze klasse de IDie interface. Verder moeten we hier niets aanpassen, omdat deze klasse al een int Roll() heeft.
public class Die : IDie //<--
{
private static Random random = new Random();
public int Roll()
{
return random.Next(6) + 1;
}
}4. Mijn NumberGame klasse heeft nu een "tight coupling" met Die. Deze Die klasse geeft een random getal terug. Ik wil de NumberGame klasse kunnen testen met een IDie waarbij ik zelf kan kiezen welk resultaat deze terug geeft. Ik maak gebruik van dependency injection en pas mijn NumberGame klasse als volgt aan:
public class NumberGame
{
private IDie die; //<--
public NumberGame(IDie die) //<--
{
this.die = die; //<--
}
public int RateGuess(int guess)
{
int result = die.Roll();
if (result == guess)
{
return 2;
}
if (result - 1 == guess || result + 1 == guess)
{
return 1;
}
return 0;
}
}We kunnen nu via de constructor onze NumberGame klasse met eender welke dobbelsteen laten werken die IDie implementeert. Dit principe noemen we Dependency Injection.
Misschien willen we een dobbelsteen met een waarde van 1 tot 12? Of één die we visueel op het scherm zien rollen? Ik maak mijn code dus niet enkel testbaar, ik werk ook dependencies weg en zorg zo voor code die makkelijker aanpasbaar is.
5. Als we nu proberen compileren krijgen we de volgende foutmelding: "There is no argument given that corresponds to the required formal parameter 'die' of 'NumberGame.NumberGame(IDie)'"
In onze Main methode moeten we nu een IDie meegeven aan onze NumberGame klasse. Ik gebruik in mijn code de Die klasse. Alles werkt dan weer zoals het was, met dat verschil dat ik nu via de constructor van NumberGame kan bepalen welke IDie ik wil gebruiken.
6. Mijn test werkt nu ook niet meer. Hier moet ik aan de NumberGame constructor ook een IDie meegeven. We zullen hiervoor een nieuwe klasse aanmaken.
Voeg een nieuwe klasse toe en geef deze de naam DieMock.
Deze klasse laten we IDie implementeren en moet dus een Roll methode hebben. We werken deze als volgt uit:
Deze code lijkt op het eerste zicht misschien een beetje vreemd maar dit is exact wat we nodig hebben. Als ik een nieuwe instance wil maken van deze klasse moet in ik de constructor een getal meegeven. Dit is het resultaat dat de dobbelsteen steeds zal teruggeven.
7. Ik kan mijn test nu als volgt implementeren:
Nu weet ik dat de dobbelsteen 5 zal teruggeven. Nu zal de test dus steeds correct werken. Als de dobbelsteen 5 teruggeeft en ik raad 5, dan krijg ik 2 punten.
Ik kan nu ook de volgende test implementeren:
En de laatste test:
"Moqing"
In het bovenstaande voorbeeld maakten we een DieMock klasse. Deze klasse zal de dobbelsteen vervangen als we het systeem willen testen. Over deze klasse hebben we zelf de controle. Het zou ons heel wat werk kosten om voor elke klasse die we maken ook een Mock klasse te voorzien en in deze klasse al het nodige gedrag te coderen. Gelukkig zijn er frameworks die deze taak voor ons uitvoeren. Wij zullen Moq gebruiken.
1. Om te beginnen voegen we via NuGet de Moq package toe:
Onder je test project rechter klik je op Dependencies (of rechtstreeks op je project)
Klik op "Manage NuGet Packages..."
Klik op browse
Zoek Moq
Selecteer Moq
Klik op install
Klik op OK
De Package is nu toegevoegd aan ons test project
In plaats van onze DieMock klasse te gebruiken zullen we nu het Moq framework gebruiken om dit object aan te maken:
2. We werken onze test nu als volg uit:
De gemarkeerde coderegel is wat vreemd gestructureerd. We gebruiken hier voor het eerst een lambda expressie. De structuur van een lambda ziet er als volgt uit (x => x.iets) of (x => iets(x)). Je moet niet x gebruiken, je kan hier eenders welke variabele voor kiezen, maar net zoals we in een for-loop meestal int i laten staan, gebruiken we x in lambda expressies. Even een voorbeeld van iets waarmee wel al bekend zijn. We kunnen op List<T> verschillende methoden aanroepen waaraan we een lambda expressie kunnen meegeven. Hier zie je een voorbeeld van code die hetzelfde doet.
Omdat die nu een Mock variabele is kunnen we Setup aanroepen. Deze methode vraagt ook voor een lambda expressie. Setup zorgt ervoor dat we een methode kunnen mocken. Om dit te doen moeten we eerst ons element selecteren: We zetten dus eerst x => x om aan te geven dat we iets willen uitvoeren.

Als we er een punt achter zetten zien we dat we alle methoden van Die te zie krijgen.

We willen Roll mocken zodat we steeds 5 terugkrijgen ipv een random waarde. Dit doen we door achter Roll().Returns(5) aan te roepen.

Dit zorgt ervoor dat als we, tijdens de test, ergens in de code Roll() aanroepen, we steeds de waarde 5 terugkrijgen!
3. Onze NumberGame klasse moet de dobbelsteen exact één keer werpen als ik de RateGuess methode aanroep. De Roll methode van de Die klasse moet dus exact één keer worden aangeroepen. We implementeren deze tests als volgt:
Verify zorgt ervoor of onze Roll methode wordt uitgevoerd. Je kan als extra parameter meegeven of dit verschillende keren gebeurt of (minstens) 1 keer.

Times heeft verschillende opties waaruit je kan kiezen.

We gaan niet te diep in op lambda expressions. Je moet ze wel begrijpen en kunnen toepassen in de context van Moq, maar we verwachten niet dat je nu alle code met lambda expressies gaat schrijven. Wil je meer info? Kijk gerust even hier, maar let erop; dit is wel wat complexer en geen verplichte leerstof!
Je kan nog zoveel meer doen met Moq dan wat we hier laten zien (bvb in je Setup vastleggen dat je een Exception werpt wanneer je een methode aanroept om te kijken of je exceptionhandling goed is). Probeer zelf wat uit en zoek online op.
Last updated