Testen van MVC applicatie in .Net

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Waster
  • Registratie: September 2006
  • Laatst online: 14-04 17:49
Ik ben bezig met een mvc project en een deel van de functionaliteit is af. We willen een deel vastleggen in unit tests, zodat we bij eventuele wijzigingen weten dat bepaalde modules nog steeds correct werken. We werken in .net met mvc 3 en het entity framework. De views is vooral GUI gerelateerd en gaan we natuurlijk niet unit testen. Aan de andere kant is de database zelf en het model geconstrueerd met het entity framework. Aangezien dit gegenereerd is zien we niet veel reden om dit te gaan testen.

We willen eigenlijk de controllers testen. Hier zitten queries in en veel logica en hier wordt ook het model voor de view opgebouwd. We willen dus eigenlijk testen of het model dat de controllers opbouwen correct is. Het probleem is dat de controllers de view aanroepen en een pagina als result geven. Dus niet het model dat wordt opgebouwd. Dus valt er nog iets binnen het mvc model te (unit)testen zonder volledig van dit model af te wijken? De standaard integratie testen gaan we uiteraard wel doen. Maar integratie testen dekken ons dus niet in als er door kleine technische wijzigingen in de ene class iets omvalt in de andere.

Acties:
  • 0 Henk 'm!

Verwijderd

[b][message=39018443,noline]...en hier wordt ook het model voor de view opgebouwd. We willen dus eigenlijk testen of het model dat de controllers opbouwen correct is.
Wat roept nu eigelijk wat?
Want eerst zeg je dat de controller het model aanmaakt, en dan zeg je het model de controller aanmaakt.

Ik denk dat je hier last hebt het fat controller anti-pattern: er zit te veel logica in de controller.

Je zegt ook dat je queries in de controller zitten, dat lijkt me ook al de verkeerde plaats te zijn.

Verplaats je logica naar de models, voeg een data access layer toe, gebruik interfaces zodat je bepaalde zaken kan mocken en/of stubben in je unittests.

Acties:
  • 0 Henk 'm!

  • Rickets
  • Registratie: Augustus 2001
  • Niet online

Rickets

Finger and a shift

Waster schreef op zondag 30 september 13:13
Het probleem is dat de controllers de view aanroepen en een pagina als result geven. Dus niet het model dat wordt opgebouwd.
Niet per se. De action methods in je controller geven geen pagina terug, maar een ViewResult.
Je controller is niet meer dan een class met methods die je kan aanroepen in je unittest: je maakt een instantie van je controller, roept een action method aan, en kijkt of je een ViewResult terugkrijgt. Verder heeft een ViewResult een property Model die je in je unittest kan uitlezen en controleren.

Terzijde: ditzelfde principe geldt natuurlijk ook voor andere types ActionResult, zoals bijvoorbeeld RedirectResult.

If some cunt can fuck something up, that cunt will pick the worst possible time to fucking fuck it up, because that cunt’s a cunt.


Acties:
  • 0 Henk 'm!

  • ThoffeGast
  • Registratie: Juni 2007
  • Laatst online: 16:15
In de praktijk zal je beginnen met wat RIckets zegt. Je kan inderdaad gewoon een controller unit testen.
Vervolgens zal je er achter komen dat je waarschijnlijk een stapje lager zou moeten unit testen en kan je je code uit elkaar gaan trekken zoals natrium zegt :)

Afhankelijk van hoe je je controller verder opbouwt is het niet zo`n groot probleem om deze apart te testen. Denk hierbij wel aan de volgende zaken:

1. Je moet tot en met de 'echte' database testen. Dus voor elke unittest moet je een lege database opzetten en deze vullen met de juiste data (afhankelijk van je scenario's natuurlijk, maar alles testen op dezelfde database is vragen om problemen als de ene test de ander aanpast..)

Je kan in ieder geval beginnen met een aparte app.config in je testproject met daarin een andere connectionstring (naar een testdatabase). En deze zou je (met wat slimme classes) per testcase kunnen vullen (gaat makkelijker als je code-first gebruikt, dan kan je de db redelijk simpel opnieuw bouwen).

Het gaat in ieder geval een beetje een zooitje worden voor betrouwbare unit tests :)

2. Verder is het afhankelijk van hoe je je data uitleest uit de POST bijvoorbeeld. In sommige gevallen moet je de FormCollection misschien 'mocken', of je Session object (is allemaal ook mogelijk maar maakt een simpele test altijd ingewikkelder), of eventuele inloggegevens etc.

Maar goed ik ga er vanuit dat je docent wel gaat zeuren als je zegt unit tests te gebruiken, en dan vervolgens je halve applicatie als een unit te beschouwen :) (dat doe ik namelijk wel).

succes in ieder geval, het zal een leerzame ervaring worden :)

Acties:
  • 0 Henk 'm!

  • Waster
  • Registratie: September 2006
  • Laatst online: 14-04 17:49
Bedankt voor de feedback. Ik wist niet dat de model terugkwam bij een viewresult, maar dan valt inderdaad te testen wat ik wil. Vaak zaten die echter verstopt achter een actionresult, omdat ik ook error afhandeling doe, maar dan heb je geen model. Maar casten naar een viewresult lost dit op :)

We hebben rekening gehouden dat we ook controle moeten hebben over de database. We dachten eigenlijk om een vaste set data in een aparte database te doen en deze te testen. Maar het is beter om het per testcase te doen. Dat is zeker waar. Ik zal er even over puzzelen wat de mogelijkheden zijn.
Verwijderd schreef op dinsdag 02 oktober 2012 @ 12:49:
[...]

Wat roept nu eigelijk wat?
Want eerst zeg je dat de controller het model aanmaakt, en dan zeg je het model de controller aanmaakt.

Ik denk dat je hier last hebt het fat controller anti-pattern: er zit te veel logica in de controller.

Je zegt ook dat je queries in de controller zitten, dat lijkt me ook al de verkeerde plaats te zijn.

Verplaats je logica naar de models, voeg een data access layer toe, gebruik interfaces zodat je bepaalde zaken kan mocken en/of stubben in je unittests.
Ik heb de link bekeken en wat verder gezocht en ben een aantal keer domain service layer tegengekomen. Dat is een nieuw concept voor mij, maar het zou wel een aantal problemen oplossen waar ik nu tegen aanloop in mijn huidige project. Al heb ik het zonder te weten al ergens toegepast. Slechts een deel van de database is toegankelijk op de frontend en dit is met booleans in de database opgeslagen. Hier heb ik al een layer voor gemaakt zodat queries op die laag worden uitgevoerd ipv overal die check uit te voeren. Ik kan deze laag uitbreiden, zodat er geen enkele query meer in de controller zit. Dat maakt zowel de controllers als de domain service laag makkelijker te testen.

Acties:
  • 0 Henk 'm!

  • ThoffeGast
  • Registratie: Juni 2007
  • Laatst online: 16:15
Waster schreef op woensdag 03 oktober 2012 @ 22:26:
We hebben rekening gehouden dat we ook controle moeten hebben over de database. We dachten eigenlijk om een vaste set data in een aparte database te doen en deze te testen. Maar het is beter om het per testcase te doen. Dat is zeker waar. Ik zal er even over puzzelen wat de mogelijkheden zijn.
Er is natuurlijk geen reden om alles op dezelfde manier (en/of even grondig) te testen. Wat wij over het algemeen doen (budget is niet oneindig):

1. De belangrijke en complexe logic hebben we altijd een tussenlaag (zonder UI koppeling etc) waarin we deze belangrijke functie(s) onderbrengen. Zo kunnen we ze later niet alleen vanuit bijvoorbeeld een andere applicatie draaien (misschien een windows service), maar kunnen we deze ook makkelijk los testen.

2. We hebben altijd een onderliggend 'data provider' object (meestal 1 per soort entity). Dat kan inderdaad zoiets zijn als die service laag (er zijn 100 namen voor ongeveer dezelfde ideeën). Hierop kunnen we ook weer (indien nodig) individuele queries testen. Zeker met entityframework willen we nog wel eens met linq iets maken, wat in de conversie naar sql niet goed gaat, of wat niet de gewenste sql of resultaten oplevert.

3. De niet belangrijke logic (denk aan simpele invoer van bijvoorbeeld landcodes) gooien we gewoon in een door visual studio gegenereerde controller met directe entity framework queries (wel met custom code templates). Als we deze zaken al willen testen, dan doen we dat inderdaad met unittests op de controller. En controleren of we een redirectResult terugkrijgen (= gelukt) of niet (=niet gelukt). Eventueel kunnen we dan nog in de database controleren of het goed is opgeslagen etc. Hier zit ook verder geen tussenlaag tussen controller en database (oe wat slecht!...)

4. Voor bijna alle objecten die iets met de database doen, hebben we een constructor overload waar we de dbcontext aan kunnen meegeven. Hiermee kan je meerdere dingen doen:

- bepalen welke context (en dus welke testdatabase) je wilt gebruiken, en die meegegeven. Je kan namelijk een context aanmaken en een connectiestring meegeven (los van je app/web.config).

- een Mock context gebruiken (je kan bijvoorbeeld werken met de interface IDbcontext ipv context). Zo kan je gewoon onderliggend een testcontext aanmaken met een paar lists en geheel zonder database werken.

Maar tegenwoordig gebruiken we alleen nog maar "code first" met entityframework. Voordeel hiervan is dat je de database al in code aanmaakt, en dus ook alle minimaal noodzakelijke data. Voor elke test laten we de context de database opnieuw genereren (zitten een aantal standaard functies in dbcontext voor). En vervolgens vullen we hem met de gewenste default data, en/of vullen we specifieke testdata. (alles binnen 1 unit test).

Volgens mij kan je deze functies ook gebruiken met de Model first aanpak (maar dat zou je even moeten checken). Je hoeft dan en geen aparte databases op te zetten, en je hoeft geen mock object te maken. De unittest gooit gewoon netjes voor elke test eerst de database leeg en doet dan zijn ding (en kan jij even koffie halen).

Ow en 1 laatste tip: beschrijf bij elke test goed WAT je nou eigenlijk test. Er is niets vervelender dan dat je een keer iets van werking aanpast, en dat je dan een paar unittests tegenkomt die failen, maar dat je geen idee hebt wat ze nou eigenlijk precies wilde testen. .. natuurlijk is dat altijd de schuld van een collega ;)
Pagina: 1