Dette er et utdrag fra Unit Testing Succinctly eBook, av Marc Clifton, gitt vennlig av Syncfusion.
I denne artikkelen skal vi begynne å snakke om noen av de avanserte emnene som følger med enhetstesting. Dette inkluderer ting som syklometrisk kompleksitet, metoder, felt og refleksjon.
Syklometrisk kompleksitet er et mål på antall uavhengige baner gjennom koden din. Koddekningstesting er ment å sikre at testene dine utfører alle mulige kodebaner. Kode dekning testing er tilgjengelig i Visual Studio Test, Premium og Ultimate versjoner. Kode dekning er ikke en del av NUnit. En tredjepartsløsning, NCover, er også tilgjengelig.
For å illustrere dette problemet, vurder denne koden, som analyserer kommandolinjeparametere:
offentlig statisk klasse CommandLineParser /// /// Returnerer en liste over alternativer basert på brute force parsing /// av kommandolinjevalg. Funksjonen returnerer /// angitte alternativer og opsjonsparameteren hvis nødvendig. /// offentlig statisk ordbokParse (streng cmdLine) Ordbok alternativer = ny ordbok (); streng [] elementer = cmdLine.Split ("); int n = 0; mens (n < items.Length) string option = items[n]; string param = String.Empty; if (option[0] != '-') throw new ApplicationException("Expected an option."); if (option == "-f") // Has the parameter been supplied? if (items.Length <= n + 1) throw new ApplicationException("Filename not specified."); param = items[n + 1]; // Is it a parameter or another option? if (param[0] == '-') throw new ApplicationException("Filename not specified."); ++n; // Skip the filename option parameter. options[option] = param; ++n; return options;
og et par enkle tester (merk at disse testene slipper kodebanene som fører til at unntak blir kastet):
[TestFixture] offentlig klasse CodeCoverageTests [Test] public void CommandParserTest () Dictionaryalternativer = CommandLineParser.Parse ("- a -b"); Assert.That (options.Count == 2, "Count forventes å være 2"); Assert.That (options.ContainsKey ("- a"), "Forventet alternativ '-a'"); Assert.That (options.ContainsKey ("- b"), "Forventet alternativ '-b'"); [Test] public void FilenameParsingTest () Dictionary alternativer = CommandLineParser.Parse ("- f foobar"); Assert.That (options.Count == 1, "Count forventes å være 1"); Assert.That (options.ContainsKey ("- f"), "Forventet alternativ '-f'"); Assert.That (alternativer ["- f"] == "foobar");
La oss nå se på hva en kodetekningsprøve kan se ut, først ved å skrive en fattig manns kodetilpasningshjelp:
offentlig statisk klasse dekning offentlig statisk liste dekningspoeng get; sett; statisk statisk tomgang Tilbakestill () CoveragePoints = ny liste (); [Conditional ("DEBUG")] statisk statisk tomt Set (int coveragePoint) CoveragePoints.Add (coveragePoint);
Vi trenger også en enkel utvidelsesmetode; Du vil se hvorfor i et minutt:
offentlig statisk klasse ListExtensions offentlig statisk bool HasOrderedItems (denne listen elementList, int [] elementer) int n = 0; foreach (int jeg i itemList) if (i! = items [n]) return false; ++ n; returnere sann;
Nå kan vi legge til dekningsposisjoner i vår kode, som blir kompilert inn i applikasjonen når den er samlet i DEBUG-modus (de dristige røde linjene hvor det er lagt til):
offentlig statisk klasse CommandLineParser /// /// Returnerer en liste over alternativer basert på brute force parsing /// av kommandolinjevalg. Funksjonen returnerer /// angitte alternativer og opsjonsparameteren hvis nødvendig. /// offentlig statisk ordbokParse (streng cmdLine) Ordbok alternativer = ny ordbok (); streng [] elementer = cmdLine.Split ("); int n = 0; mens (n < items.Length) Coverage.Set(1); // WE ADD THIS COVERAGE SET-POINT string option = items[n]; string param = String.Empty; if (option[0] != '-') throw new ApplicationException("Expected an option."); if (option == "-f") Coverage.Set(2); // WE ADD THIS COVERAGE SET-POINT // Has the parameter been supplied? if (items.Length <= n + 1) throw new ApplicationException("Filename not specified."); param = items[n + 1]; // Is it a parameter or another option? if (param[0] == '-') throw new ApplicationException("Filename not specified."); ++n; // Skip the filename option parameter. options[option] = param; ++n; return options;
Og nå kan vi skrive følgende testarmatur:
[TestFixture] offentlig klasse CommandParserCoverageTests [SetUp] public void CoverageSetup () Coverage.Reset (); [Test] offentlig tomgang CommandParserTest () Dictionaryalternativer = CommandLineParser.Parse ("- a -b"); Assert.That (Coverage.CoveragePoints.HasOrderedItems (ny [] 1, 1)); [Test] public void FilenameParsingTest () Dictionary alternativer = CommandLineParser.Parse ("- f foobar"); Assert.That (Coverage.CoveragePoints.HasOrderedItems (ny [] 1, 2));
Legg merke til hvordan vi nå ignorerer de faktiske resultatene, men sørger for at de ønskede kodeblokkene blir utført.
En enhetstest bør nok bare omhandle offentlige felt og metoder. Motargumentet for dette er at for å teste hele implementeringen, tilgang til beskyttede eller private felt, for å hevde deres tilstand, og evnen til å måle testbeskyttede eller private metoder kreves. Med tanke på at det sannsynligvis ikke er ønskelig å eksponere de fleste lavnivåberegninger, og det er akkurat de metodene man vil teste, er det mest sannsynlig at det skal testes minst beskyttede eller private metoder. Det er flere alternativer tilgjengelig.
Dette eksemplet illustrerer konseptet:
offentlig klasse DoesSomething #if TEST offentlig #else privat #endif void SomeComputation ()
Selv om dette er gjennomførbart, produserer det stygg kildekode og utfører den alvorlige risikoen for at noen faktisk kan kalle metoden med symbolet TEST definert, bare for å oppdage at hans eller hennes kode brytes i en produksjonsbygg hvor TEST-symbolet er udefinert.
Som et alternativ, hvis metodene er beskyttet, bør du vurdere å avlede en testklasse:
offentlig klasse DoesSomethingElse protected void SomeComputation () offentlig klasse DoesSomethingElseTesting: DoesSomethingElse public void TestSomeComputation () base.SomeComputation ();
Dette gjør at du kan instantiere den avledede testklassen og få tilgang til en beskyttet metode via en offentlig eksponert metode i underklassen.
Til slutt kan man bruke refleksjon for private metoder eller forseglede klasser. Følgende illustrerer en privat metode og utfører denne metoden via refleksjon i en test:
offentlig klasse DoesSomething private void SomeComputation () [TestClass] offentlig klasse DoesSomethingTest [TestMethod] offentlig tomt SomeComputationTest () DoesSomething ds = new DoesSomething (); Skriv t = ds.GetType (); MethodInfo mi = t.GetMethod ("SomeComputation", BindingFlags.Instance | BindingFlags.NonPublic); mi.Invoke (ds, null);