Unit Testing Succinctly Visual Studio

Dette er et utdrag fra Unit Testing Succinctly eBook, av Marc Clifton, gitt vennlig av Syncfusion.

En enhetstest består av to ting:

  • En klasse som representerer testfestet.
  • Metoder i klassen som representerer enhetstester.

Visual Studio lager automatisk en stub for et testprosjekt, som er hvor vi skal starte.

Opprette et enhetstestprosjekt i Visual Studio

Enhetstester plasseres vanligvis i et eget prosjekt (som resulterer i en tydelig sammenstilling) fra søknadskoden. I Visual Studio 2008 eller 2012 kan du opprette et enhetstestprosjekt ved å høyreklikke på løsningen og velge Legg til etterfulgt av Nytt prosjekt fra hurtigmenyen:

Legge til et nytt prosjekt

Fra dialogboksen som vises, velg en Test prosjekt:

VS2008 nytt testprosjekt VS2012 nytt testprosjekt

Visual Studio 2008 vil opprette en stubfil, "UnitTest1.cs" (hvis du valgte C # -språket), med en rekke nyttige kommentarer i stubben. Visual Studio 2012 skaper en mye terser stub:

bruker system; bruker Microsoft.VisualStudio.TestTools.UnitTesting; navneområde VS2012UnitTestProject1 [TestClass] offentlig klasse UnitTest1 [TestMethod] offentlig tomrom TestMethod1 ()  

For Visual Studio 2008 Brukere

Visual Studio 2008 vil også opprette en TestContext-klasse - dette eksisterer ikke lenger i VS2012, og vi vil ignorere det - bruk forrige stub fra VS2012 i stedet.

Slett også "ManualTest1.mht" -filen, ellers blir du bedt om å velge testresultater og skrive inn testnotater manuelt.

Testutstyr

Legg merke til at klassen er dekorert med attributtet TestClass. Dette definerer test fixturen-en samling av testmetoder.

Testmetoder

Legg merke til at metoden er dekorert med attributtet TestMethod. Dette definerer en metode som testfestet vil løpe.


Assert-klassen

Assert-klassen definerer følgende statiske metoder som kan brukes til å verifisere beregning av metoden:

  • AreEqual / AreNotEqual
  • AreSame / AreNotSame
  • IsTrue / IsFalse
  • IsNull / IsNotNull
  • IsInstanceOfType / IsNotInstanceOfType

Mange av disse påstandene er overbelastet, og det anbefales at du gjennomgår den fulle dokumentasjonen som Microsoft gir.

Grunnlag for å gjøre en påstand

Vær oppmerksom på at følgende eksempler bruker VS2008.

Påstandene er ryggraden i hver test. Det er en rekke påstander som man kan gjøre angående resultatene av en test. Til å begynne med skriver vi en enkel påstand som sier "en er lik en," med andre ord en truisme:

[TestClass] offentlig klasse UnitTest1 [TestMethod] public void TestMethod1 () Assert.IsTrue (1 == 1);  

Kjør testen, som skal resultere i "Passed":

Enkel påstand

AreEqual / AreNotEqual

AreEqual og AreNotEqual metoder sammenligner:

  • objekter
  • dobles
  • singler
  • strenger
  • skrevet data

De har formen av å sammenligne den forventede (den første parameteren) med den faktiske (den andre parameterverdien). Med hensyn til enkle og doble verdier kan "innenfor en viss nøyaktighet" spesifiseres. Til slutt har alle overbelastninger muligheten til å vise en melding (eventuelt formatert) hvis påstanden mislykkes.

Med hensyn til objektets likestilling, sammenligner denne metoden om forekomstene er identiske:

offentlig klasse AnObject  [TestMethod] public void ObjectEqualityTest () AnObject object1 = nytt AnObject (); AnObject object2 = nytt AnObject (); Assert.AreNotEqual (objekt1, objekt2);  

Den foregående testen går, da objekt1 og objekt2 ikke er like. Men hvis klassen tilsidesetter Equals-metoden, er likestillingen basert på sammenligningen laget av Equals-metoden som ble implementert i klassen. For eksempel:

offentlig klasse AnObject public int SomeValue get; sett;  offentlig overstyring bool Like (objekt obj) return NoenValue == ((AnObject) obj) .SomeValue;  [TestMethod] public void ObjectEqualityTest () AnObject object1 = nytt AnObject () SomeValue = 1; AnObject object2 = nytt AnObject () SomeValue = 1; Assert.AreEqual (objekt1, objekt2);  

AreSame / AreNotSame

Disse to metodene verifiserer at forekomster er det samme (eller ikke). For eksempel:

[TestMethod] public void SamenessTest () AnObject object1 = nytt AnObject () SomeValue = 1; AnObject object2 = nytt AnObject () SomeValue = 1; Assert.AreNotSame (object1, object2);  

Selv om klassen AnObject overstyrer Equals-operatøren, passerer den foregående testen fordi forekomster av de to objektene er ikke det samme.

IsTrue / IsFalse

Disse to metodene tillater deg å teste sannheten til en verdi sammenligning. Fra et lesbarhetsperspektiv benyttes IsTrue og IsFalse-metodene vanligvis for verdi sammenligninger, mens AreEqual og AreSame vanligvis brukes til å sammenligne forekomster (objekter).

For eksempel:

[TestMethod] public void IsTrueTest () AnObject object1 = nytt AnObject () SomeValue = 1; Assert.IsTrue (object1.SomeValue == 1);  

Dette bekrefter verdien av en eiendom.

IsNull / IsNotNull

Disse to testene verifiserer om en gjenstand er null eller ikke:

[TestMethod] public void IsNullTest () AnObject object1 = null; Assert.IsNull (object1);  

IsInstanceOfType / IsNotInstanceOfType

Disse to metodene verifiserer at et objekt er en forekomst av en bestemt type (eller ikke). For eksempel:

offentlig klasse AnotherObject  [TestMethod] offentlig tomrom TypeOfTest () AnObject object1 = new AnObject (); Assert.IsNotInstanceOfType (object1, typeof (AnotherObject));  

mangelfulle

Assert.Inclusive-metoden kan brukes til å angi at enten testen eller funksjonaliteten bak testen ennå ikke er implementert, og derfor er testen utilsiktet.

Hva skjer når en påstand mislykkes?

Når det gjelder test av Visual Studio-enhet, kaster Assert-metoden en AssertFailedException når et påstand mislykkes. Dette unntaket bør aldri håndteres av testkoden.


Andre assertjonsklasser

Det er to andre påstandsklasser:

  • CollectionAssert
  • StringAssert

Som navnene antyder, gjelder disse påstandene henholdsvis på samlinger og strenger.

Samling påstander

Disse metodene er implementert i Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert-klassen. Vær oppmerksom på at samlingsparameteren i disse metodene forventer at samlingen skal implementere ICollection (kontrast dette med NUnit, som forventer IEnumerable).

AllItemsAreInstanceOfType

Denne påstanden verifiserer at objekter i en samling er av samme type, som inkluderer avledede typer av den forventede typen, som illustrert her:

offentlig klasse A  offentlig klasse B: A  [TestClass] offentlig klasse Samlingstester [TestMethod] offentlig tomgang InstancesOfTypeTest () Liste punkter = ny liste () nytt punkt (1, 2), nytt punkt ); Liste elementer = ny liste() ny B (), ny A (); CollectionAssert.AllItemsAreInstancesOfType (poeng, typeof (punkt)); CollectionAssert.AllItemsAreInstancesOfType (elementer, typeof (A));   

AllItemsAreNotNull

Denne påstanden verifiserer at objekter i samlingen ikke er null.

AllItemsAreUnique

Denne testen sikrer at objekter i en samling er unike. Hvis man sammenligner strukturer:

[TestMethod] public void AreUniqueTest () Liste punkter = ny liste () nytt punkt (1, 2), nytt punkt (1, 2); CollectionAssert.AllItemsAreUnique (poeng);  

strukturene er sammenlignet med verdi, ikke for eksempel - den foregående testen feiler. Men selv om klassen tilsidesetter Equals-metoden:

offentlig klasse AnObject public int SomeValue get; sett;  offentlig overstyring bool Like (objekt obj) return NoenValue == ((AnObject) obj) .SomeValue;  

denne testen passerer:

[TestMethod] public void AreUniqueObjectsTest () Liste elementer = ny liste() new AnObject () SomeValue = 1, ny AnObject () SomeValue = 1; CollectionAssert.AllItemsAreUnique (eks);   

AreEqual / AreNotEqual

Disse testene hevder at to samlinger er like. Metodene inkluderer overbelastninger som lar deg gi en komparator metode. Hvis et objekt tilsidesøker Equals-metoden, brukes denne metoden til å bestemme likestilling. For eksempel:

[TestMethod] public void AreEqualTest () Liste itemList1 = ny liste() new AnObject () SomeValue = 1, ny AnObject () SomeValue = 2; Liste itemList2 = ny liste() new AnObject () SomeValue = 1, ny AnObject () SomeValue = 2; CollectionAssert.AreEqual (itemList1, itemList2);   

Disse to samlingene er like fordi klassen AnObject overstyrer Equals-metoden (se forrige eksempel).

Merk at for å passere påstanden, må listerne være av samme lengde og anses ikke like om lister er identiske bortsett fra i en annen rekkefølge. Sammenlign dette med AreEquivalent påstanden som er beskrevet nedenfor.

AreEquivalent / AreNotEquivalent

Denne påstanden sammenligner to lister og vurderer lister over like elementer som ekvivalente uansett rekkefølge. Dessverre ser det ut til å være en feil i implementeringen, da denne testen mislykkes:

[TestMethod] public void AreEqualTest () Liste itemList1 = ny liste() new AnObject () SomeValue = 1, ny AnObject () SomeValue = 2; Liste itemList2 = ny liste() new AnObject () SomeValue = 2, ny AnObject () SomeValue = 1; CollectionAssert.AreEquivalent (itemList1, itemList2);     Visual Studio er Equivalent Bug  

med feilmeldingen:

CollectionAssert.AreEquivalent mislyktes. Den forventede samlingen inneholder 1 forekomst (er) av. Den faktiske samlingen inneholder 0 forekomst (er). 

Mens NUnits implementering av denne påstanden passerer:

NUnits AreEquivalent Works Korrekt

Inneholder / DoesNotContain

Denne påstanden verifiserer at et objekt er inneholdt i en samling:

[TestMethod] public void ContainsTest () Liste itemList = ny liste() new AnObject () SomeValue = 1, ny AnObject () SomeValue = 2; CollectionAssert.Contains (itemList, new AnObject () SomeValue = 1);   

bruker Equals-metoden (hvis overstyrt) for å utføre likestiltesten.

IsSubsetOf / IsNotSubsetOf

Denne påstanden verifiserer at den første parameteren (delmengden) er inneholdt i den andre parameterens samling (supersettet).

[TestMethod] offentlig tomrum SubsetTest () string subset = "abc"; streng superset = "1d2c3b4a"; CollectionAssert.IsSubsetOf (subset.ToCharArray (), superset.ToCharArray ());  

Vær oppmerksom på at undersettprøven ikke tester for rekkefølge eller sekvens - det tester bare om elementene i delmengelisten er inneholdt i supersettet.

String påstander

Disse metodene implementeres i Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert-klassen:

  • inneholder
  • Kamper / DoesNotMatch
  • Starts / EndsWith

Disse blir diskutert neste.

inneholder

Inneholder metoden hevder at delmengden (merk at dette er den andre parameteren) finnes i strengen (den første parameteren). For eksempel passerer denne testen:

[TestClass] offentlig klasse StringTests [TestMethod] public void ContainsTest () string subset = "abc"; streng superset = "123abc456"; StringAssert.Contains (superset, subset);  

Kamper / DoesNotMatch

Denne metoden hevder at strengen (den første parameteren) samsvarer med regex-mønsteret som er gitt i den andre parameteren.

Starts / EndsWith

Denne metoden hevder at strengen (den første parameteren) enten starter med eller slutter med en annen streng (den andre parameteren).


unntak

Unntak kan testes uten å skrive tryllestokk rundt testmetoden. For eksempel, mens du kan skrive dette:

[TestMethod] public void CatchingExceptionsTest () prøv Divide (5, 0);  fangst (ArgumentOutOfRangeException) // Ta imot unntaket som gyldig.  

Det er mye mer lesbart å bruke ExpectedException attributtet på testmetoden:

[TestMethod] [ExpectedException (typeof (ArgumentOutOfRangeException)) offentlig tomgang BadParameterTest () Divide (5, 0);  

Andre nyttige egenskaper

Det er noen ekstra attributter som er nyttige for å kjøre en serie tester, samt individuelle tester som forbedrer gjenbrukbarheten og lesbarheten til enhetstestkoden.

Oppsett / Teardown

Visual Studio enhet testmotor gir fire ekstra metoden attributter:

  • ClassInitialize
  • ClassCleanup
  • TestInitialize
  • TestCleanup

Disse egenskapene foregår og følger utførelsen av alle tester i fixturen (klassen), så vel som før og etter hver test i fixturen.

Vær oppmerksom på at metodene som er dekorert med dette attributtet må være statiske.

ClassInitialize

Hvis en metode er dekorert med dette attributtet, kjøres koden i metoden før alle tester i fixturen kjøres. Merk at denne metoden krever en TestContext-parameter.

Denne metoden er nyttig for å tildele ressurser eller instantiere klasser som alle testene i fixturen stole på. En viktig overveielse med ressurser og objekter opprettet under oppstart av innretningen er at disse ressursene og objektene skal betraktes som skrivebeskyttet. Det er ikke tilrådelig for tester å endre tilstanden til ressurser og gjenstander som andre tester er avhengige av. Dette inkluderer tilkoblinger til tjenester som databaser og webtjenester hvis forbindelsen kan bli satt i ugyldig tilstand som følge av en feil i en test, og dermed ugyldiggjør alle de andre testene. Videre er rekkefølgen der tester kjører ikke garantert. Hvis du endrer tilstanden til en ressurs og objekt som er opprettet i oppstartspartiets initialisering, kan det føre til bivirkninger, avhengig av rekkefølgen der tester kjøres.

ClassCleanup

En metode dekorerte med dette attributtet er ansvarlig for deallokering av ressurser, lukkekoblinger, etc., som ble opprettet under klassens initialisering. Denne metoden vil alltid utføres etter å ha kjørt testene i fixturen, uavhengig av suksessen eller feilen i testene selv.

TestInitialize

På samme måte som ClassInitialize-attributtet, vil en metode som er dekorert med dette attributtet, bli utført for hver test før du kjører testen. Et av formålene med dette attributtet er å sikre at ressurser eller objekter som er tildelt av ClassInitialize-koden, initialiseres til en kjent tilstand før de kjører hver test.

TestCleanup

I tillegg til TestInitialize-attributtet vil metoder som er dekorert med TestCleanup, bli utført ved ferdigstillelsen av hver test.

Setup og Teardown Flow

Følgende kode viser strømmen av fixtur og testoppsett og teardown i forhold til de faktiske testene:

[TestClass] offentlig klasse SetupTeardownFlow [ClassInitialize] offentlig statisk tomrom SetupFixture (TestContext kontekst) Debug.WriteLine ("Fixture Setup.");  [ClassCleanup] offentlig statisk tomrom TeardownFixture () Debug.WriteLine ("Fixture Teardown.");  [TestInitialize] public void SetupTest () Debug.WriteLine ("Test Setup.");  [TestCleanup] offentlig tomgang TeardownTest () Debug.WriteLine ("Test Teardown.");  [TestMethod] public void TestA () Debug.WriteLine ("Test A.");  [TestMethod] public void TestB () Debug.WriteLine ("Test B.");  

Kjører denne monteringen resulterer i følgende feilsøkingsspor:

Fixture Setup. Test Setup. Test A. Test Teardown. Test Setup. Test B. Test Teardown. Fixture Teardown. 

Som illustrert i det forrige eksempel, blir fixturen initialisert-deretter for hver test utføres testoppsettet og teardown-koden, etterfulgt av fixtur-teardown på enden.


Mindre hyppig brukte attributter

Følgende avsnitt beskriver mindre vanlige attributter.

AssemblyInitialize / AssemblyCleanup

Metoder dekorert med dette attributtet må være statiske og utføres når montering er lastet. Dette ber om spørsmålet - hva om forsamlingen har mer enn en testarmatur?

[TestClass] offentlig klasse Fixture1 [AssemblyInitialize] offentlig statisk tomrom AssemblyInit (TestContext kontekst) // ... noen operasjon [TestClass] offentlig klasse Fixture2 [AssemblyInitialize] offentlig statisk tomrum AssemblyInit (TestContext kontekst) // ... noen operasjon  

Hvis du prøver dette, mislykkes testmaskinen til å kjøre noen enhetstester, rapportering:

"UTA013: UnitTestExamplesVS2008.Fixture2: Kan ikke definere mer enn én metode med AssemblyInitialize attributtet inne i en enhet."

Derfor kan bare én AssemblyInitialize og One AssemblyCleanup-metoden eksistere for en montering, uavhengig av antall testarmaturer i den samlingen. Det anbefales derfor at ingen egentlige tester legges inn i klassen som definerer disse metodene:

[TestClass] offentlig klasse AssemblyFixture [AssemblyInitialize] offentlig statisk tomrum AssemblySetup (TestContext kontekst) Debug.WriteLine ("Assembly Initialize.");  [AssemblyCleanup] offentlig statisk tomrum AssemblyTeardown () Debug.WriteLine ("Assembly Cleanup.");  

som resulterer i følgende kjøringssekvens:

Montering Initialiser. Fixture Setup. Test Setup. Test A. Test Teardown. Test Setup. Test B. Test Teardown. Fixture Teardown. Montering Opprydding. 

Legg merke til tilleggsinnstillingene for opprinnelig samling og opprydding.

Overse

Denne metoden kan dekorere spesifikke metoder eller hele inventar.

Ignorer en testmetode

Hvis dette attributtet dekorerer en testmetode:

[TestMethod, Ignore] public void TestA () Debug.WriteLine ("Test A.");  

testen vil ikke løpe. Dessverre viser ikke vinduet Visual Studio Test Resultat at det er tester som for øyeblikket ignoreres:

Ignorerte test er ikke vist

Sammenlign dette med NUnit, som tydelig viser ignorerte tester:

NUnit viser ignorerte test

NUnit-displayet markerer hele testtreet som "ukjent" når en eller flere testmetoder er merket som "Ignorer".

Ignorere en testfeste

En hel fixturs metoder kan ignoreres ved å bruke Ignor-attributtet på klassenivå:

[TestClass, Ignorer] offentlig klasse SetupTeardownFlow ... etc ... 

Å tømme testbufferen

Hvis du legger til ignoreringsattributtet til en metode, kan du merke at Visual Studio fortsatt kjører testen. Det er nødvendig å fjerne testbufferen for Visual Studio for å hente endringen. En måte å gjøre dette på er å rense løsningen og gjenoppbygge den.

Eieren

Brukt til rapporteringsformål, beskriver denne attributtet personen som er ansvarlig for enhetstestmetoden.

DeploymentItem

Hvis enhetstester blir kjørt i en egen distribusjonsmappe, kan dette attributtet brukes til å spesifisere filer som en testklasse eller testmetode krever for å kunne kjøre. Du kan angi filer eller mapper for å kopiere til distribusjonsmappen og eventuelt angi målbanen i forhold til distribusjonsmappen.

Beskrivelse

Brukt til rapportering, gir dette attributtet en beskrivelse av testmetoden. Merkelig, dette attributtet er bare tilgjengelig på testmetoder og er ikke tilgjengelig på testklasser.

HostType

For testmetoder brukes denne egenskapen til å spesifisere verten som enhetstesten skal kjøre inn.

Prioritet

Denne egenskapen brukes ikke av testmotoren, men kan brukes, via refleksjon, av din egen testkode. Bruken av dette attributtet er tvilsomt.

WorkItem

Hvis du bruker Team Foundation Server (TFS), kan du bruke dette attributtet på en testmetode for å angi arbeidselement ID som er tildelt av TFS til den spesifikke enhetstesten.

CssIteration / CssProjectStructure

Disse to attributter brukes i forhold til TeamBuild og TestManagementService og lar deg spesifisere en prosjektherreasjon som testmetoden tilsvarer.


Parameterisert testing med DataSource-attributten

Microsofts enhetstestmotor støtter CSV-, XML- eller database datakilder for parameterisert testing. Dette er ikke akkurat ekte parameterisert testing (se hvordan NUnit implementerer parameterisert testing) fordi parametrene ikke sendes til enhetstestmetoden, men må hentes ut fra datakilden og sendes til testmetoden. Imidlertid er muligheten til å laste testdata til en DataTable fra en rekke kilder, nyttig for kjøringstestautomatisering.

CSV datakilde

En tekstfil med komma-separert verdi kan brukes til en datakilde:

Numerator, Neminator, ExpectedResult 10, 5, 2 20,5, 4 33, 3, 11 

og brukes i en testmetode:

[TestClass] offentlig klasse DataSourceExamples public TestContext TestContext get; sett;  [TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.CSV", "C: \\ temp \\ csvData.txt", "csvData # txt", DataAccessMethod.Sequential)] offentlig tomgang CsvDataSourceTest () int n = Convert.ToInt32 (TestContext.DataRow ["Numerator"]); int d = Convert.ToInt32 (TestContext.DataRow ["Neminator"]); int r = Konverter.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Det resulterer i følgende utgang:

n = 10, d = 5, r = 2 n = 20, d = 5, r = 4 n = 33, d = 3, r = 11 

Vær oppmerksom på at testresultatvinduet ikke viser parameterløpene (kontrast dette til NUnit):

Parameteriserte testresultater

Det er imidlertid åpenbare fordeler ved ikke å vise hver testkombinasjon, spesielt for store datasett.

XML-datakilde

Gitt en XML-fil som:

    

et eksempel på bruk av en XML-datakilde for en enhetstest er:

[TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.XML", "C: \\ temp \\ xmlData.xml", "Row", DataAccessMethod.Sequential)] offentlig tomgang XmlDataSourceTest () int n = Konverter .ToInt32 (TestContext.DataRow [ "Teller"]); int d = Convert.ToInt32 (TestContext.DataRow ["Neminator"]); int r = Konverter.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Merk at annet enn datakildeattributtparametrene, testkoden er den samme.

Database Datakilde

En databasetabell kan også brukes som datakilde. Gitt et bord som:

Databasetabell som datakilde

og data:

Databasetestdata

Et eksempel på testmetode som bruker disse dataene ser ut som:

[TestMethod] [DataSource ("System.Data.SqlClient", "Datakilde = INTERACX-HP; Initial Catalog = UnitTesting; Integrert Security = True"; "DivideTestData"; DataAccessMethod.Sequential)] offentlig tomgang XmlDataSourceTest () int n = Konverter.ToInt32 (TestContext.DataRow ["Numerator"]); int d = Convert.ToInt32 (TestContext.DataRow ["Neminator"]); int r = Konverter.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Igjen, vær oppmerksom på at testmetoden selv er den samme. Det eneste vi har gjort her, er å endre DataSource-definisjonen.

TestProperty Attribut

MSDN-dokumentasjonen for dette attributtet illustrerer å deklarere et TestProperty-navnverdierpar og deretter, ved å bruke refleksjon, anskaffe navnet og verdien. Dette ser ut til å være en ubarmhjertig måte å skape parameteriserte tester på. 

Koden, som er beskrevet på Craig Anderas blogg, for å bruke TestProperty-attributtet til å parameterisere testinitialiseringsprosessen, påvirker ikke TestContext.Properties-samlingen på Visual Studio 2008 eller Visual Studio 2012.