Inledning
Unit tests, eller enhetstester på svenska, låter kanske inte så spännande. Du kanske redan har en uppfattning om vad det handlar om? Oavsett vad man kanske redan tycker så är enhetstester något som är bra för dig, din kod och de runt i kring dig som någon gång kanske måste arbeta med din kod. På sikt skulle vi påstå att du blir en bättre programmerare av att skriv enhetstester.
Vad är Unit testing?
Enhetstester är små bitar kod som testar så att t.ex. en viss metod i din klass gör det som förväntas av den. Koden som gör testet skall vara liten just för att varje test endast skall testa en liten del. Är den liten så blir koden oftast lättläslig, begriplig och fri från buggar. (för vem har tid att skriva enhetstester åt enhetstesterna? ..)
Testerna kan sedan köras, manuellt eller automatiskt, och kommer att rapportera om de lyckades eller inte.
I praktiken så samlar man sina enhetstester i ett separat projekt som refererar den "riktiga" koden så att klasser och dess beteende kan testas. Det kan se ut såhär:
Här har vi ett class library döpt till MittBibliotek. Testprojektet som har lagts till i mappen tests har vi döpt till samma namn fast med .Test på slutet. Detta är en rätt vanlig sätt att namnge testprojekten på.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MittBibliotek.Test
{
[TestClass]
public class MinKlassTest
{
[TestMethod]
public void Add2ShouldReturnCorrectValue()
{
//Arrange
var underTest = new MinKlass();
//Act
int result = underTest.Add2(3);
//Assert
Assert.IsTrue(result == 5);
}
}
}
Testklasser dekoreras med attributet TestClass och testmetoder i klassen med TestMethod. Det finns en hel rad andra attribut som styr testerna men vi spar det till kommande artiklar. Exemplet ovan använder MS Test. Andra test-ramverk har andra namn på sina attribut.
Testerna kan köras under menyn Test->Run->All tests. Resultatet presenteras som:
Nyttan med tester
Testerna kan på ett vis ses som dokumentation över din kod. Den visar tydligt hur dina klasser skall användas samt vilket som är förväntat beteende. Dokumentationen växer också i takt med implementationen. Målet är att ha tester som täcker 100% av koden. Detta kallas för code coverage och kan ses som en kvalitetsnivå.
Tester kan också ses som ett skydd mot så kallade regressionsfel. Om du går in och ändrar kod i A så slutar B att fungera. Har du tester så kommer dessa fel i B att upptäckas. Du kan alltså fånga upp fel som du annars hade introducerat i koden.
Det går snabbt att köra testerna! När du väl har skrivit ett test så har du det för all framtid. Enhetstester är inga temporära tester som skrivs "bara för att testa". Nej! enhetstester blir en del av din utveckling.
Du upptäcker fel tidigt. När tester skrivs så brukar en del fel visa sig direkt. Du slipper alltså vänta tills någon manuellt kör en testrunda, eller ännu värre att kunden upptäcker fel vid leverans.
Enhetstester anses vara "billigare" än integrationstester, systemtester samt funktionella tester. Detta utesluter givetvis inte nämnda sorters tester. Buggfixar blir också lättare att genomföra efter en leverans då risken & oron för regressionsfel är mindre.
Kvalitén samt designen på koden kan bli bättre då man hela tiden utmanas att skriva om och förbättra koden i det steg som kallas refactoring, mer om detta senare.
Test driven development (TDD)
TDD = test driven development är väldigt populärt just nu. När man pratar om systemutveckling på en högre nivå så är det svårt att undvika att prata om olika sorters projektmetodiker. En metodik som är otroligt populär är agil utveckling (oftast någon form av SCRUM). TDD är en viktig del i agil utveckling. Vad innebär egentligen TDD?
TDD förespråkar att, efter initial planering, systemdesign, kravställning etc., börja skriva testfall. Testfall skrivs före implementation. Det kan tyckas bakvänt men tänk efter lite.
Ofta hamnar man i fällan att lösa uppgiften så snabbt som möjligt. Genast börjar man tänka på implementationen. Efter ett tag fungerar det.. trodde man! Helt plötsligt dyker det upp nya krav eller att man missat krav som gör att din implementation inte längre håller måttet. Du måste skriva om.
En utvecklare som jobbar efter TDD låter testerna driva implementationen. Testerna skrivs för att beskriva hur det skall fungera. Hur implementationen görs bryr man sig inte om. Om alla tester klaras så har vi en fungerade implementation helt enkelt. Utvecklaren skriver ju givetvis implementationen men den blir oftast mycket enklare att göra när testerna är skrivna.
Inom TDD brukar man följa 3 steg; red, green och refactor.
Red är när testerna du skrivit fallerar. Nu börjar du implementera så att du klarar testerna. Du tillför nya saker i din kod.
När testerna är gröna (green) är det dags för refactor, refaktorering. Det är nu du snyggar till din kod, lägger den på rätt ställe, byter kanske namn på metoder och klasser. Gör den enklare och mer lättläst om du kan! Du skall inte tillföra något nytt i detta steg.
Nu börjar man om med att utöka testerna eller kanske förfinar dem. Du hamnar då i "red" igen och allt börjar om.
Tips när man skriver tester
- Skriv korta tester!
- Använd beskrivande namn i testmetoderna, t.ex. ItShouldThrowExceptionWhenNullIsPassed kan tyckas långt men är helt ok.
- Dela tydligt in testet i arrange (, assume), act, assert
- Försök undvika att ha flera Assert i samma test. Skriv hellre fler tester.
Verktyg för Unit testing
I C# är de vanligaste ramverken Microsoft Test (MS Test) eller Nunit. Som nybörjare spelar valet av dessa mindre roll. Som avancerad programmerare är det mer en smaksak. Tester skrivna som fungerar i det ena ramverket fungerar oftast i det andra. Endast små skillnader existerar vanligtvis.
Vi kommer att använda oss av MS Test i våra exempel då det följer med Visual Studio från början sedan ett tag tillbaka.
Det en rad plugins i Visual Studio som hjälper dig när du skall skriva och köra testerna. Bl.a. ReSharper, NCrunch och Mighty Moose. De två senare har en finess som kallas continuous test som kontinuerligt kör dina tester under tiden du kodar. Mighty Moose är gratis, övriga har en 30-dagars fri testperiod.
Det finns plugins som mäter code coverage, alltså hur stor del av din kod som du har enhetstester på. Exempel är dotCover och NCover samt även tidigare nämnda NCrunch.
En uppsjö av ramverk som hjälper dig att skriva bättre och mer avancerade tester, främst FakeItEasy men också enklare ramverk som Should. Alla finns givetvis som paket via NuGet.
Vi lämnar denna diskussion om ramverk och verktyg till en framtida artikel.
Avslutning
Hoppas du tyckte denna artikel gav dig en bra inledning inom ämnet. Inom systemutveckling så finns det många "religioner" som säger hur du skall bedriva din utveckling, där TDD är en av dem. Vårt råd är att aldrig lyssna för mycket på endast en källa. Skaffa dig en bred erfarenhet och bilda dig en egen uppfattning. Jobbar man i projekt som följer en metodik är det kanske bäst att ligga lite lågt om man har en helt avvikande uppfattning om hur saker och ting skall genomföras.
Vi kan avsluta med att konstatera att enhetstester blir allt vanligare inom all sorts utveckling.
Tack för att du tagit dig tiden och skrivit på Svenska! Det är skönt att få blanda lite modersmål med all engelska kring ämnet!
Tack!