Testing
NUnit
Børre Stenseth
Moduler>Kodetesting>C#Testing>NUnit

Testing C#

Hva
Testing av C#-kode

Vi holder oss i "unit-tradisjonen" og bruker et verktøy som heter NUnit [1] .

Ukedag

Vi tar et enkelt eksempel og konsentrerer oss i første omgang om det rent tekniske med å sette opp en test. Jeg forutsetter at NUnit er lastete ned og installert.

Det vi ønsker å gjøre er å implementere en klasse Day med en angivelse av dato (måned, år og dag) i konstruktoren. Vi ønsker å gi en slik dag en property WeekDay som returnerer hvilken ukedag den aktuelle datoen faller på. Vi finner en algoritme som forsøksvis gjør dette og får følgende:

using System;
using System.Collections.Generic;
using System.Text;
namespace ukedag
{
    class Day
    {
        int m_year=2000;
        int m_month=1;
        int m_day=1;
        [STAThread]
        static void Main(string[] args)
        {
        }
         public Day(int y, int m, int d)
        {
            m_year = y;
            m_month = m;
            m_day = d;
        }
        public int WeekDay
        {
            // according to Tomohiko Sakamoto
            // http://www.eskimo.com/~scs/C-faq/q20.31.html
            get
            {
                int[] t = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
                if (m_month < 3)
                    m_year--;
                return (m_year + m_year / 4 - m_year / 100 + m_year / 400 + t[m_month - 1] + m_day) % 7;
            }
        }
    }
}

Det vi ønsker å teste er altså algoritmen som produserer WeekDay. Vi legger til en testklasse, Tester, i samme prosjekt:

using System;
namespace ukedag
{
    using NUnit.Framework;
    [TestFixture]
    public class Tester
    {
        Day oneDay;
        public Tester()
        {
            // TODO: Add constructor logic here
        }
        [SetUp]public void Init()
        {
            oneDay = new Day(2005,12,24);
        }
        [Test]public void IsDaySaturday()
        {
            Assert.IsTrue(6 == oneDay.WeekDay);
        }
    }
}

For at vi nå skal kunne bygge prosjektet, må vi legge til nunit.framework til references i Solution Explorer i Visual Studio. Hvis du har installert NUnit vil du finne denne blandt de "globale" assembliene. Etter å ha bygget prosjektet kan vi starte NUnit-Gui og åpne den exe-fila vi har bygget. Da vil det se omtrent slik ut:

nunit1

Vi kan klikke Run og få kjørt testen(e). Du kan endre dato, bygge, reload i NUnit og teste igjen.

Nå har vi lagt selve klassen og testklassen i samme prosjekt og vi bygget slik at testklassen er en del av den .exe-fila vi har bygget. Det kan du sjekke med f.eks. programmet Reflector, se referanser. Dette virker ikke hensiktsmessig hvis vi utvikler store testbatterier. Det vi imdlertid skal merke oss er at det er ingen avhengighet mellom klassen og testklassen. Day er ikke avhengig av Tester. Vi kan derfor fjerne Tester fra prosjektet og bygge på nytt, og vi kan legge Tester til hvis vi skal teste mer eller bygge ut Tester.

Så til selve testingen. Det virker ikke som en særlig overbevisende test at vi har slått fast at julaften 2005 faller på en lørdag. Sett i det store bildet virker det utilfredstillende. Nå er det ingenting i veien for at vi kan bygge ut Tester med nye datoer og nye tester. Hva slags tester vi kan lage går fram av dokumentasjonen for NUnit. Uansett hva vi gjør så kan vi ikke komme lenger enn at vi kan teste algoritmen med datoer der vi kjenner fasiten. Vi ken på basis av et antall tester konkludere med at algoritmen virker fornuftig, men lenger kommer vi ikke. En komplett test ville måtte bestå av alle datoer ( i det området vi er interesserte i). En slik test ville vi like greitt, eller ugreitt, kunne skrives direkte uten noen testmekanisme som NUnit. Det eksempelet vi har valgt er på mange måter spesielt i det det demonstrerer testingens utilstrekkelighet snarere enn effektivitet.

I dette tilfellet er det dessuten slik at vi har "fasiten" .Net implementerer jo en mekanisme for å finne ukedag i klassen DateTime. Vi kan derfor skrive om klassen og testen slik:

using System;
using System.Collections.Generic;
using System.Text;
namespace ukedag
{
    class Day
    {
        int m_year=2000;
        int m_month=1;
        int m_day=1;
        [STAThread]
        static void Main(string[] args)
        {
        }
         public Day(int y, int m, int d)
        {
            m_year = y;
            m_month = m;
            m_day = d;
        }
        public int WeekDay
        {
            // according to Tomohiko Sakamoto
            // http://www.eskimo.com/~scs/C-faq/q20.31.html
            get
            {
                int[] t = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
                if (m_month < 3)
                    m_year--;
                return (m_year + m_year / 4 - m_year / 100 + m_year / 400 + t[m_month - 1] + m_day) % 7;
            }          
        }
        public int Year { get { return m_year; } }
        public int Month { get { return m_month; } }
        public int DayInMonth { get { return m_day; } }
    }
}

og Tester

using System;
namespace ukedag
{
    using NUnit.Framework;
    [TestFixture]
    public class Tester
    {
        Day oneDay;
        public Tester()
        {
            // TODO: Add constructor logic here
        }
        [SetUp]public void Init()
        {
            oneDay = new Day(2006,11,3);
        }
        [Test]
        public void IsDayAccordingToDateTime()
        {
            try
            {
                DateTime d = new DateTime(oneDay.Year, oneDay.Month, oneDay.DayInMonth);
                int myDay = oneDay.WeekDay;
                Assert.IsTrue(
                        ((d.DayOfWeek == DayOfWeek.Sunday) && (myDay == 0))
                     || ((d.DayOfWeek == DayOfWeek.Monday) && (myDay == 1))
                     || ((d.DayOfWeek == DayOfWeek.Tuesday) && (myDay == 2))
                     || ((d.DayOfWeek == DayOfWeek.Wednesday) && (myDay == 3))
                     || ((d.DayOfWeek == DayOfWeek.Thursday) && (myDay == 4))
                     || ((d.DayOfWeek == DayOfWeek.Friday) && (myDay == 5))
                     || ((d.DayOfWeek == DayOfWeek.Saturday) && (myDay == 6))
                     );
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Assert.IsTrue(false);
            }
        }
    }
}

Hvis vi betrakter DateTime som fasit, har vi forbedret situasjonen noe. Nå burde vi kunne lage "random" datoer og få testet algoritmen. Merk at testen (unntakstesten) på ulovlige datodata nå er plassert i Tester for illustrasjonens skyld. En slik test burde selvsagt også ligge i konstruktøren til Day.

Kan vi stole på dateTime for alle tenkelige datoer ?

Referanser
  1. NUnitsourceforge.netsourceforge.net/projects/nunit14-03-2010
  1. NUnitsourceforge.netsourceforge.net/projects/nunit14-03-2010
Vedlikehold

B. Stenseth, desember 2005

(Velkommen) Moduler>Kodetesting>C#Testing>NUnit (JStesting)