Jeg kommer ofte bort i prosjekter som bruker mock-rammeverk for å skrive enhetstester. Og selv om det er bra at man har tatt i bruk et verktøy som gjør det lettere å skrive enhetstester, slik at det i hvert fall blir skrivet noen enhetstester, er ikke jeg særlig glad i disse mock-rammeverk.

Jeg skjønner filosofien bak mock-rammeverket: de gjør det lettere å skrive enhetstester ved å gi utviklere enkle måter å erstatte og simulere avhengigheter på. Ofte er det slik at én linje med mock-kode er nok til å erstatte en hel klasse med dummy kode. Problemet er bare at når man først har begynt å bruke et mock-rammeverk, så blir det ikke med bare én eller to linjer med mock-kode per enhetstest, men gjerne flere og flere. Og da er det ikke lenger så farlig å legge til flere avhengighetet i kildekoden – de kan jo lett håndteres i enhetstestene med enda en linje mock-kode!

Til slutt havner man i en situasjon der man trenger ti–tjue linjer med mock-kode bare for å få enhetstestene i gang. Hva som egentlig testes, og om man virkelig fortsatt tester kildekode og ikke bare mock-rammeverket, blir etter hvert mer og mer uklart. I noen prosjekter har man gått så langt at man har begynt å lage egne klasser med hjelpermetoder for å samle opp all mock-kode som man trenger for å sette opp en enhetstest. Eller det vil si, der man kan kalle bare fire–fem slike hjelpermetoder for å få enhetstestene satt opp riktig. Det virker ikke riktig i mine øyne: er man da ikke tilbake til det man ønsket å unngå, bare et hakk verre?

Jeg har derfor begynt å se på bruk av et mock-rammeverk som en code smell: her har man prøvd seg på en kjapp løsning for å få bedre testdekning, uten å angripe det virkelige problemet, nemlig at koden er for lite testbar og/eller har for mange avhengigheter som man ikke klarer å rydde opp i. Jeg har sett unntak der det var riktig å bruke et mock-rammeverk for å få skrevet enhetstester. Et eksempel var et gammelt prosjekt med legacy-kode som var så stygg at man ikke kunne instansiere en eneste klasse uten å ha en database på plass populert med omtrent halve universet. Men slike prosjekt har virkelig vært unntak.

Hva er da den riktige måten å gjøre det på? Skriv interfaces for alle dine avhengigheter, og lag dummy implementasjoner som er så ryddige at de kan gjenbrukes fra enhetstest til enhetstest. Ofte går det like raskt som å bruke et mock-rammeverk, særlig når man klarer å gjenbruke disse dummy implementasjonene fra enhetstest til enhetstest. I tillegg dokumenterer interfacene avhengighetene dine på en god måte. Jeg har mange ganger skrevet hashbaserte fasader til filsystemet, klokker som jeg kan stille akkurat som jeg vil, eller en Random som ligner veldig mye på XKCD 221. Men min erfaring er at det går raskere til slutt, bare man tar det litt saktere der det er viktig. Og god enhetstesting er viktig.

Om Filip Van Laenen

Filip er en meget erfaren arkitekt og rådgiver som har jobbet med et bredt spekter av arkitekturmessige problemstillinger. Han er en av våre fremste teknologiske rådgivere og leder for teknologiske forum, et rådgivningsorgan for Computas` linje- og prosjektorganisasjon.

5 thoughts on “Slutt å bruke mock-rammeverk!

  1. skjønner problemet og det er godt formulert, men jeg litt usikker på om jeg skjønner løsningen. løsningene er, i min ringe forståelse, (i) avhengigheter i egne grensesnitt og (ii) implementer selv. ingen av de de tingene gjør vel noe per se med (a) mock koden begynner å bli omfattende og (b) koden en tester er skrevet på en lite testbar måte. hvis (a) er komplisert så er vel (ii) tilsvarende komplisert? refaktorering av griset i (b) burde vel være sentralt? men det er kanskje implisitt i (i)? kanskje et eksempel ville hjulpet oss som er litt trege i opptaket.. 😎

    1. Det er riktig at dersom (a) er et problem, så er (ii) også et problem. Men min erfaring er at mocking (a) virker litt som en smertestiller, som tillater at griseriet i (b) kan bli mye større før det virkelig begynner å smerte enn under (ii). M.a.o. det er bedre at det smerter tidlig, slik at du får ryddet opp i problemene før de blir for store.

  2. jeg må si jeg er litt uenig, hvis du skal for hvert interface punche ut en dummy klasse sitter du igjen med en hau med stygge klasser med ingen innhold annet enn en simpel return for de få metodene i interfacet du trenger pluss en hau med metoder som virkelig bare er død, det er nettopp dette et mock verktøy gjør for deg. Hvis det er sånn at du har mye mocking i en enhetstest er det bare et sted det er årsaken: enheten ( koden ) du prøver å teste er alt for stor, da må du tilbake til koden og stykke den opp, da vil enhetstesten din endre seg deretter.

    1. Det er riktig at hvis man bare implementerer disse interfacene med en masse stygge dummy klasser, så har man ikke oppnådd noe. Da har det sannsynligvis til og med blitt verre om man hadde brukt et mock-rammeverk. Poenget er at de implementerende klassene burde være gjenbrukbare fra enhetstest til enhetstest, slik at det ikke blir så mange av dem.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *