SlaMidtfyn's Wiki site : UnitTest

Home :: Categories :: PageIndex :: RecentChanges :: RecentlyCommented :: Login/Register

Unit test


Indholdsfortegnelse

Forord
Denne artikel omhandler emnet unit test. Årsagen til at jeg har skrevet den, er at jeg selv savnede noget information om hvordan og hvornår jeg skulle lave unit tests. Artiklen tager udgangspunkt i en række andre artikler samt nogle personlige erfaringer. Artiklen er bygget ud fra de konklusioner som jeg selv har udledt ved at læse om emnet, og er ikke nødvendigvis 100% korrekt på alle punkter. Hvis du som læser ser nogle forkerte antagelser eller beskrivelser er du velkommen til at kontakte mig enten på mail slamidtfyn@gmail.com, via twitter http://twitter.com/slamidtfyn eller skrive en kommentar nederst i artiklen.

Alle eksempler er lavet i VS2008 med VB.NET og ved brug af VS indbyggede test framework. Eksemplerne er lavet som "quick n' dirty" løsninger for ikke at skulle lave alt for komplekse eksempler.

Hvorfor
Der er faktisk en del gode grunde til at unit teste sin kode. Jeg vil her nævne nogle af dem, som jeg mener er de mest væsentlige:

Hvordan
For at man kan teste sin kode, er der en vigtig faktor der skal tages hensyn til. Koden skal være testbar! - dvs at man som minimum bør adskille sin kode, således at forretningslogik er adskilt fra koden der driver brugerfladen. Hvis forretningslogikken er indlejret i button_click() funktioner osv er det meget svært, hvis ikke umuligt, at lave unit test. Læs mere om forretningslogik her: WikiPedia:Business_logic_layer. Men dette er ikke helt nok, nok kan man nu lave test af klasser og metoder men for at hjælpe sig selv, er der nogle flere ting at tage hensyn til. Hvis klasser og metoder skal være nemme at teste, kan man med fordel følge principperne for DependencyInjection (WikiPedia:Dependency_injection). Kort fortalt går det ud på, at sørge for at man ikke opretter en masse objekter, som en del af en klasse konstruktor.

Prøv for eksempel at se denne klasse:
Product.vb
Public Class Product
    Public Sub New()
        Me.Wheels.Add(New ProductDetail() With {.Name = "Hjul"})
        Me.Bolts.Add(New ProductDetail() With {.Name = "Bolt"})
    End Sub

...
End Class


Her bliver der som en del af konstruktoren oprettet nogle data, fordi på det tidspunkt hvor klassen blev lavet, var det en forudsætning at klassen skulle have nøjagtigt disse data. Hvis man skulle teste oprettelsen af denne klasse, vil man skulle tage hensyn til at netop disse variable blev oprettet. Hvis denne klasse skulle testes ville man fx lave følgende test:

ProductTest.vb
  <TestMethod()> _
    Public Sub ProductConstructorWheelsTest()
        Dim target As Product = New Product
        Assert.IsTrue(target.Wheels.Count = 1)
    End Sub
   
    <TestMethod()> _
    Public Sub ProductConstructorBoltsTest()
        Dim target As Product = New Product
        Assert.IsTrue(target.Bolts.Count = 1)
    End Sub


Men denne test er meget sårbar, hvis fx der pludselig ændres i krav, så produktet skal have 2 hjul og dette laves som en del af konstruktoren vil testen fejle. Derfor vil en bedre løsning være at lave en factory klasse der opretter de korrekte data til produktet, således at produktet kan testes uafhængigt af dette. Dette er et utroligt simpelt eksempel, der kunne have været en række andre objekter der blev oprettet, som gav disse problemer fx en reference til en datakilde eller en logfil, som man ikke kan eller vil teste.

For at man får den rigtige mening med unit tests, bør man følge princippet omkring Test Driven Development (WikiPedia:Test-driven_development). Her er der lagt vægt på at man bør skrive sin test, inden man skriver den funktionalitet der skal testes. Det kan dog være en smule besværligt at gøre dette i VS2008, da man herved lidt mister muligheden for at oprette test klasser automatisk ud fra sin forretningslogik. Jeg bruger selv følgende metode til at omgå dette.

  1. Opret klasse med kun en konstruktor og ikke andet
  2. Opret test klasse ved hjælp af funktionerne i VS
  3. Skriv test og tilføj eventuelle parametre og properties til klassen og se testen fejle
  4. Skriv kode og se testen gå godt
  5. Opret eventuelle metoder i klassen uden parametre og indhold
  6. Opret test metoder ved hjælp af funktionerne i VS
  7. Skriv test og tilføj eventuelle parametre til metoden for at kunne kompilere, men testen fejler stadig
  8. Skriv koden til metoden og se testen gå godt.

Fordelen ved at gøre dette, er at man automatisk undgår mange af de ting der gør det svært at test sin kode, fordi man får tænkt testen ind med det samme. Se eventuelt mit eksempel på en sådan fremgangsmåde her TddDemo.

Hvornår

Unit test bør udføres på generelt alt forretningslogik. Der er sikkert nogle situationer, hvor det ikke er så nemt at skrive sin unit test da man i mange tilfælde vil være afhængig af at en række af objekter er bundet til datakilder, der skal være udfyldt med nogle fornuftige data. Men det bør ikke være en undskyldning for ikke at teste, nærmere en udfordring til at lave sin forretningslogik så simpel og testbar som muligt.

Hvis man i sin forretningslogik har taget udgangspunkt i de krav som der stilles til softwaren, skal det som udgangspunkt være muligt at teste sin kode. Som et eksempel kunne man tage en applikation, hvortil der er stillet følgende krav:


Her vil det være logisk, at lave 2 unit tests. En der kontrollerer om der kan oprettes et objekt af typen kunde og en test af om alle informationer bliver kontrolleret.

Først er her Client klassen der skal testes:
Client.vb
Public Class Client
    Public Sub New()
        'Dummy constructor
    End Sub

    Public Function Validate() As Boolean
        If String.IsNullOrEmpty(_Name) Then Return False
        If Not _Age.HasValue Then Return False

        'All parameters are correct.
        Return True
    End Function
...
End Class


Og her er de to tests:

ClientTest.vb
  <TestMethod()> _
    Public Sub ClientConstructorTest()
        Dim target As Client = New Client
        Assert.IsInstanceOfType(target, GetType(Logic.Client), "Client creation failed")
    End Sub

    <TestMethod()> _
    Public Sub ValidateTest()
        Dim target As Client = New Client With {.Name = "Test", .Age = 25}
        Dim actual As Boolean
        actual = target.Validate
        Assert.IsTrue(actual, "Validate client failed")
    End Sub


En lille morsom note til denne test er, at den faktisk fejlede første gang jeg kørte den. Jeg havde ved en fejl oprettet propertien Name som en integer og fik derved fjernet denne fejl allerede inden jeg havde anvendt min Client klasse til noget.

Referencer

Artikler og videoer om emnerne i denne artikel:
http://www.developer.com/net/net/article.php/11087_3636501_2
http://www.youtube.com/watch?v=RlfLCWKxHJ0
http://www.youtube.com/watch?v=wEhu57pih5w
http://visualstudiomagazine.com/features/article.aspx?editorialsid=2489
http://msdn.microsoft.com/en-us/magazine/dd263069.aspx
http://msdn.microsoft.com/en-us/magazine/cc163665.aspx

Kommentarer
blog comments powered by Disqus

There are no comments on this page. [Add comment]

Valid XHTML 1.0 Transitional :: Valid CSS :: Powered by WikkaWiki
Page was generated in 0.1270 seconds