onsdag den 20. marts 2013

Den gode unittest


Jeg er tit blevet spurgt om: "Hvordan laver man en god unittest?".
Det er forholdsvis nemt at svare på: "Ved at sikre at ens unittest tester alle dele af den unit man ønsker at teste" :-)

Det er jo stadig noget abstrakt, så jeg tænker det er en god ide at uddybe lidt.
En unit test lever på det nederste niveau i V-modellen. En unit i den sammenhæng er en klump kode der ikke kan skilles mere ad end den er, sædvanligvis er dette en metode på et objekt. Det kan dog også være andre ting, men det er sjældent, så vi holder fast ved en  metode på et objekt.

Lige som alt andet test vil ens unittest følge disse fire trin, hvor det fjerde er valgfrit.

  1. Lav en opsætning af dit testmiljø sådan det afspejler den afstuppede virkelighed som du vil teste i. Dette involvere bl.a. initialisering af mocks ot stubs. I en unittest ønsker vi ikke at kunne nogen som helst form for integration, hverken imellem de enkelte units eller til andre system enheder, så hvis der afhængigheder af den art, skal de helst stub'es eller mock'es.
  2. Kald den metode der skal testes. Dette er sædvanligvis så simpelt som en enkelt linie kode der rent faktisk kalder metoden.
  3. Tjek testmiljøets tilstand efter testmetoden har været kaldt. Verificer at tilstanden er som forventet efter kald af den testede kode. Dette kan være en meget simpel eller en meget kompleks opgave alt efter hvilken metode det er der blev kaldt i step 2. Man skal altid gøre det så umiddelbart som muligt hvorfor det er man tjekker på de ting man gøre, og hvad det er man tjekker for. Kodekommentarer er guld værd her.
  4. Oprydning. Dette step skal der helst ikke være noget af i en unittest. Vi skal helst have mock'et og stub'et vores verden af i step 1, og så er oprydningen minimal, og alt efter programmeringssprog, tit så simpel som at lade stubs og mocks forsvinde ud af scope sådan at garbage collectoren klarer oprydningen. I unitintegrationstest ser verden dog ret tit noget anderledes ud omkring oprydning, så hvis du finder dig selv i en situation hvor der er meget oprydning skal du nik se op om du er ved at skrive en unitintegrationstest mere ens en unittest.
Implementation af unit testen kan ske på mange måder, men for de fleste sprog i dag er der udviklet frameworks som laver at det kedelige arbejder for os, og jeg vil klart anbefale at brug et af de etablerede frameworks frem for at begynde selv at opfinde noget. Selv hælder jeg meget til XUnit familien da den altid har dækket et hvert behov jeg har haft.
Dette gælder også for stub og mock frameworks. Brug noget eksisterende frem for at finde på noget selv. 

I java verdenen mener jeg at man kan klare sig med følgende:
  • JUnit (http://junit.org) - komplet unittest framework som tilmed er bygget ind i de fleste IDE'er
  • Mockito (https://code.google.com/p/mockito) er alt hvad man behøver når det kommer til at mock'e objekter. Dette framework går det også super nemt at tjekke sine mock objekters tilstand efter kald af den testede metode, hvilket er mindst lige så vigtigt som at kunne lave sin mock objekter nemt.
  • Hamcrest (https://code.google.com/p/hamcrest) kan hjælpe utroligt meget med at gøre ens testmetoder mere læsbare ved at udtrykke sin kode mere som en sætning på almindelig engelsk.
Der er mange der har mange meninger om hvordan man laver den gode unittest, og der stor variation i opskrifterne for unittest i de forskellige sprog. Jeg har forsøgt at holde mig på et konceptuelt niveau der gerne skulle gælde for alle sprog, men hvis du har et eksempel på den gode unittest, skal du ikke holde dig tilbage for at lave en kommentar

Ingen kommentarer:

Send en kommentar