Enhetsprovning Succinctly Visual Studio

Detta är ett utdrag från Unit Testing Succinctly eBook, av Marc Clifton, vänligt tillhandahållen av Syncfusion.

Ett enhetstest består av två saker:

  • En klass som representerar testfixturen.
  • Metoder i klassen som representerar enhetstest.

Visual Studio skapar automatiskt en stub för ett testprojekt, vilket är var vi ska börja.

Skapa ett testprojekt i Visual Studio

Enhetstester placeras vanligtvis i ett separat projekt (vilket resulterar i en distinkt sammansättning) från din ansökningskod. I Visual Studio 2008 eller 2012 kan du skapa ett enhetstestprojekt genom att högerklicka på lösningen och välja Lägg till följd av Nytt projekt från popup-menyn:

Lägga till ett nytt projekt

Välj en dialogruta i dialogrutan som visas Testa projekt:

VS2008 Nytt testprojekt VS2012 Nytt testprojekt

Visual Studio 2008 skapar en stubfil, "UnitTest1.cs" (om du valde C # -språk), med en mängd användbara kommentarer i stubben. Visual Studio 2012 skapar en mycket terser stub:

använder system; använder Microsoft.VisualStudio.TestTools.UnitTesting; namnrymd VS2012UnitTestProject1 [TestClass] offentlig klass UnitTest1 [TestMethod] public void TestMethod1 ()  

För Visual Studio 2008-användare

Visual Studio 2008 skapar också en TestContext-klass - det här finns inte längre i VS2012 och vi ignorerar det - använd föregående stub från VS2012 istället.

Ta även bort filen "ManualTest1.mht", annars kommer du att bli ombedd att välja testresultat och skriva in testnoteringar manuellt.

Test fixtures

Observera att klassen är dekorerad med attributet TestClass. Detta definierar testfixturen-en samling testmetoder.

Testmetoder

Observera att metoden är dekorerad med attributet TestMethod. Detta definierar en metod som testet fixturen kommer att springa.


Assert-klassen

Assert-klassen definierar följande statiska metoder som kan användas för att verifiera en metods beräkning:

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

Många av dessa påståenden är överbelastade och det rekommenderas att du granskar den fullständiga dokumentationen som Microsoft tillhandahåller.

Grunderna för att göra ett påstående

Observera att följande exempel använder VS2008.

Påståenden är ryggraden i varje test. Det finns en rad olika påståenden som man kan göra angående resultaten av ett test. Till att börja med skriver vi en enkel påstående som säger "en är lika med en", med andra ord en truism:

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

Kör provet, vilket ska resultera i "Passed":

Enkel påstående

AreEqual / AreNotEqual

AreEqual och AreNotEqual metoderna jämför:

  • objekt
  • dubbel
  • singel
  • strängar
  • typad data

De har formen av att jämföra den förväntade (den första parametern) med det faktiska värdet (det andra parametern). När det gäller singel- och dubbelvärden kan "inom en viss noggrannhet" anges. Slutligen har alla överbelastningar möjlighet att visa ett meddelande (valfritt formaterat) om påståendet misslyckas.

När det gäller objekt jämlikhet jämför denna metod om instanser är identiska:

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

Det föregående testet passerar, eftersom objekt1 och objekt2 inte är lika. Om klassen däremot överträder Equals-metoden, är jämställdheten baserad på jämförelsen gjord av Equals-metoden som implementerats i klassen. Till exempel:

offentlig klass AnObject public int SomeValue get; uppsättning;  public override bool Likas (objekt obj) returnera SomeValue == ((AnObject) obj) .SomeValue;  [TestMethod] public void ObjectEqualityTest () AnObject object1 = nytt AnObject () SomeValue = 1; AnObject object2 = nytt AnObject () SomeValue = 1; Assert.AreEqual (object1, object2);  

AreSame / AreNotSame

Dessa två metoder verifierar att instanser är samma (eller inte). Till exempel:

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

Även om klassen AnObject överstyrar Equals-operatören, passerar det föregående testet eftersom instanser av de två objekten är inte samma.

IsTrue / IsFalse

Med dessa två metoder kan du testa sanningen i en värdejämförelse. Ur ett läsbarhetsperspektiv används IsTrue och IsFalse-metoderna typiskt för värde jämförelser, medan AreEqual och AreSame brukar användas för att jämföra instanser (objekt).

Till exempel:

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

Detta verifierar det värdet av en egendom.

IsNull / IsNotNull

Dessa två test verifierar om ett objekt är null eller ej:

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

IsInstanceOfType / IsNotInstanceOfType

Dessa två metoder verifierar att ett objekt är en förekomst av en viss typ (eller ej). Till exempel:

offentlig klass AnotherObject  [TestMethod] public void TypeOfTest () AnObject object1 = nytt AnObject (); Assert.IsNotInstanceOfType (object1, typeof (AnotherObject));  

Inte övertygande

Assert.Inconclusive-metoden kan användas för att ange att antingen testet eller funktionaliteten bakom testet ännu inte har genomförts och därför är testet oavslutat.

Vad händer när ett påstående misslyckas?

När det gäller test av Visual Studio-enhet, när en påstående misslyckas, kastar Assert-metoden en AssertFailedException. Detta undantag ska aldrig hanteras av din testkod.


Andra klasser för assertion

Det finns två andra påståenden:

  • CollectionAssert
  • StringAssert

Som deras namn innebär, gäller dessa påståenden på samlingar respektive strängar.

Samlingsförklaringar

Dessa metoder implementeras i klassen Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert. Observera att insamlingsparametern i dessa metoder förväntar sig att samlingen ska implementera ICollection (kontrast detta med NUnit, vilket förväntar sig IEnumerable).

AllItemsAreInstanceOfType

Denna påstående verifierar att objekt i en samling är av samma typ, vilket inkluderar härledda typer av den förväntade typen, som illustreras här:

offentlig klass A  allmän klass B: A  [TestClass] offentlig klass CollectionTests [TestMethod] public void InstancesOfTypeTest () Lista punkter = ny lista () ny punkt (1, 2), ny punkt ); Lista objekt = ny lista() ny B (), ny A (); CollectionAssert.AllItemsAreInstancesOfType (poäng, typof (Punkt)); CollectionAssert.AllItemsAreInstancesOfType (objekt, typ (A));   

AllItemsAreNotNull

Denna påstående verifierar att objekt i samlingen inte är noll.

AllItemsAreUnique

Detta test säkerställer att objekt i en samling är unika. Om man jämför strukturer:

[TestMethod] public void AreUniqueTest () Listapunkter = ny lista () ny punkt (1, 2), ny punkt (1, 2); CollectionAssert.AllItemsAreUnique (punkter);  

strukturerna jämförs med värde, inte exakt-det föregående testet misslyckas. Men även om klassen åsidosätter Equals-metoden:

offentlig klass AnObject public int SomeValue get; uppsättning;  public override bool Likas (objekt obj) returnera SomeValue == ((AnObject) obj) .SomeValue;  

detta test passerar:

[TestMethod] public void AreUniqueObjectsTest () Lista objekt = ny lista() nytt AnObject () SomeValue = 1, nytt AnObject () SomeValue = 1; CollectionAssert.AllItemsAreUnique (objekt);   

AreEqual / AreNotEqual

Dessa tester hävdar att två samlingar är lika. Metoderna inkluderar överbelastningar som gör att du kan tillhandahålla en jämförelsemetod. Om ett objekt överstyrar Equals-metoden, kommer den här metoden att användas för att bestämma jämlikhet. Till exempel:

[TestMethod] public void AreEqualTest () Lista itemList1 = ny lista() nytt AnObject () SomeValue = 1, nytt AnObject () SomeValue = 2; Lista itemList2 = ny lista() nytt AnObject () SomeValue = 1, nytt AnObject () SomeValue = 2; CollectionAssert.AreEqual (itemList1, itemList2);   

Dessa två samlingar är lika eftersom klassen AnObject överstyrar Equals-metoden (se föregående exempel).

Observera att listorna måste vara av samma längd och att de inte anses vara lika om listorna är identiska utom i en annan ordning. Jämför detta med AreEquivalent påståendet som beskrivs nedan.

AreEquivalent / AreNotEquivalent

Denna påstående jämför två listor och anser att listor med lika artiklar är likvärdiga oavsett order. Tyvärr verkar det finnas ett fel i genomförandet, eftersom det här testet misslyckas:

[TestMethod] public void AreEqualTest () Lista itemList1 = ny lista() nytt AnObject () SomeValue = 1, nytt AnObject () SomeValue = 2; Lista itemList2 = ny lista() nytt AnObject () SomeValue = 2, nytt AnObject () SomeValue = 1; CollectionAssert.AreEquivalent (itemList1, itemList2);     Visual Studio's Equivalent Bug  

med felmeddelandet:

CollectionAssert.AreEquivalent misslyckades. Den förväntade samlingen innehåller 1 händelse (r) av. Den faktiska samlingen innehåller 0 förekomst (er). 

NUnits genomförande av denna påstående passerar:

NUnits AreEquivalent Works Korrekt

Innehåller / DoesNotContain

Denna påstående verifierar att ett föremål finns i en samling:

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

använder Equals-metoden (om den är överdriven) för att utföra jämställdhetsprovet.

IsSubsetOf / IsNotSubsetOf

Denna påstående verifierar att den första parametern (delmängden) finns i den andra parameterns samling (superset).

[TestMethod] public void SubsetTest () string subset = "abc"; sträng superset = "1d2c3b4a"; CollectionAssert.IsSubsetOf (subset.ToCharArray (), superset.ToCharArray ());  

Observera att delmängdstestet inte testar för order eller sekvens - det testar enkelt om objekten i delmängdslistan finns i superset.

Strängförklaringar

Dessa metoder implementeras i Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert klass:

  • innehåller
  • Tändstickor / DoesNotMatch
  • Starts / endswith

Dessa diskuteras därefter.

innehåller

Innehållsmetoden hävdar att delmängden (notera att detta är den andra parametern) finns i strängen (den första parametern). Till exempel passerar detta test:

[TestClass] offentliga klassen StringTests [TestMethod] public void ContainsTest () string subset = "abc"; sträng superset = "123abc456"; StringAssert.Contains (superset, subset);  

Tändstickor / DoesNotMatch

Denna metod hävdar att strängen (den första parametern) matchar regexmönstret som tillhandahålls i den andra parametern.

Starts / endswith

Denna metod hävdar att strängen (den första parametern) antingen börjar med eller slutar med en annan sträng (den andra parametern).


undantag

Undantag kan testas utan att skriva provtagningsblock runt testmetoden. Till exempel, medan du kan skriva detta:

[TestMethod] public void CatchingExceptionsTest () försök Dela (5, 0);  fångst (ArgumentOutOfRangeException) // Acceptera undantaget som giltigt.  

Det är mycket lättare att använda attributet ExpectedException på testmetoden:

[TestMethod] [ExpectedException (typof (ArgumentOutOfRangeException)) public void BadParameterTest () Divide (5, 0);  

Andra användbara attribut

Det finns några ytterligare attribut som är användbara för att köra en serie test samt individuella test som förbättrar återanvändbarheten och läsbarheten hos enhetstestkodsbasen.

Setup / nedkoppling

Visual Studio enhet testmotor ger fyra ytterligare metod attribut:

  • ClassInitialize
  • ClassCleanup
  • TestInitialize
  • TestCleanup

Dessa attribut föregår och följer utförandet av alla tester inom fixturen (klassen), liksom före och efter varje test i fixturen.

Observera att metoderna dekorerade med detta attribut måste vara statiska.

ClassInitialize

Om en metod är dekorerad med det här attributet körs koden i metoden före körning av alla tester i fixturen. Observera att den här metoden kräver en TestContext-parameter.

Denna metod är användbar för att allokera resurser eller instansera klasser som alla test i fixturen är beroende av. Ett viktigt övervägande med resurser och objekt som skapats under initieringen av fixturen är att dessa resurser och objekt ska betraktas som skrivskyddade. Det är inte tillrådligt att testa för att ändra tillståndet för resurser och objekt som andra test beror på. Detta inkluderar anslutningar till tjänster som databas- och webbtjänster, vars anslutning kan sättas in i ett ogiltigt tillstånd som ett resultat av ett fel i ett test, vilket därmed ogiltigförklarar alla andra test. Dessutom är den ordning i vilken test körs inte garanterad. Att ändra tillståndet för en resurs och ett objekt som skapats i fixturinitialiseringen kan resultera i biverkningar, beroende på vilken ordning testen körs.

ClassCleanup

En metod som är dekorerad med detta attribut ansvarar för att allokera resurser, stänga anslutningar etc. som skapades under klassinitiering. Denna metod kommer alltid att utföras efter att testen har körts i fixturen, oavsett om testen är framgångsrik eller felaktig.

TestInitialize

Liknande attributet ClassInitialize utförs en metod som är dekorerad med detta attribut för varje test innan testet körs. Ett av syftena med detta attribut är att säkerställa att resurser eller objekt som tilldelas av klassinitialiserings-koden initieras till ett känt tillstånd innan de körs varje test.

TestCleanup

Komplettera attributet TestInitialize, kommer metoder som dekoreras med TestCleanup att köras vid slutförandet av varje test.

Installations- och nedrivningsflöde

Följande kod visar flödet av fixtur och test setup och teardown i förhållande till de aktuella testen:

[TestClass] public class SetupTeardownFlow [ClassInitialize] offentliga statiska tomrum SetupFixture (TestContext context) Debug.WriteLine ("Fixture Setup.");  [ClassCleanup] offentliga statiska tomrum TeardownFixture () Debug.WriteLine ("Fixture Teardown.");  [TestInitialize] public void SetupTest () Debug.WriteLine ("Test Setup.");  [TestCleanup] offentligt tomrum TeardownTest () Debug.WriteLine ("Test Teardown.");  [TestMethod] public void TestA () Debug.WriteLine ("Test A.");  [TestMethod] public void TestB () Debug.WriteLine ("Test B.");  

Körning av denna fixtur resulterar i följande felsökningspåverkan:

Fixture Setup. Testuppsättning. Test A. Test avbrott. Testuppsättning. Test B. Teardown test. Fixture Teardown. 

Som illustreras i det föregående exemplet, initieras fixturen-sedan för varje test exekverar testinställningen och avbrottskoden, följt av fixturens nedbrytning vid slutet.


Mindre ofta använda attribut

Följande avsnitt beskriver mindre vanliga attribut.

AssemblyInitialize / AssemblyCleanup

Metoder dekorerade med detta attribut måste vara statiska och exekveras när enheten är laddad. Detta ber om frågan - vad om samlingen har mer än en test fixtur?

[TestClass] public class Fixture1 [AssemblyInitialize] offentliga statiska tomrum AssemblyInit (TestContext context) // ... någon operation [TestClass] public class Fixture2 [AssemblyInitialize] public static void AssemblyInit (TestContext context) // ... någon operation  

Om du försöker detta misslyckas testmotorens körning av enhetstester, rapportering:

"UTA013: UnitTestExamplesVS2008.Fixture2: Kan inte definiera mer än en metod med AssemblyInitialize-attributet inuti en enhet."

Därför kan endast en AssemblyInitialize och One AssemblyCleanup-metod existera för en montering, oberoende av antalet testinställningar i den montering. Det rekommenderas därför att inga egentliga test sätts i klassen som definierar dessa metoder:

[TestClass] public class AssemblyFixture [AssemblyInitialize] offentlig statisk tomrum AssemblySetup (TestContext context) Debug.WriteLine ("Assembly Initialize.");  [AssemblyCleanup] offentliga statiska tomrum AssemblyTeardown () Debug.WriteLine ("Assembly Cleanup.");  

resulterande i följande utföringsföljd:

Montering Initiera. Fixture Setup. Testuppsättning. Test A. Test avbrott. Testuppsättning. Test B. Teardown test. Fixture Teardown. Montering Rengöring. 

Observera de extra monteringsinitialiserings- och uppringningssamtalen.

Ignorera

Denna metod kan dekorera specifika metoder eller hela armaturer.

Ignorera en testmetod

Om detta attribut dekorerar en testmetod:

[TestMethod, Ignorera] offentligt tomrum TestA () Debug.WriteLine ("Test A.");  

testet kör inte. Tyvärr visar inte visningspanelen i Visual Studio Test att det finns tester som för närvarande ignoreras:

Ignorerade test visas inte

Jämför detta med NUnit, vilket tydligt visar ignorerade tester:

NUnit visar ignorerad test

NUnit-displayen markerar hela testträdet som "okänt" när en eller flera testmetoder markeras som "Ignorera".

Ignorera en testfästning

En hel fixturs metoder kan ignoreras med hjälp av attributet Ignorera på klassnivå:

[TestClass, Ignorera] offentlig klass SetupTeardownFlow ... etc ... 

Rensa testcachen

Om du lägger till attributet Ignorera till en metod kan du märka att Visual Studio fortfarande kör testet. Det är nödvändigt att rensa testcachen för Visual Studio för att hämta ändringen. Ett sätt att göra detta är att rengöra lösningen och bygga om den.

Ägare

Används för rapporteringsändamål beskriver det här attributet den person som ansvarar för enhetstestmetoden.

DeploymentItem

Om enhetstesterna körs i en separat installationsmapp kan det här attributet användas för att ange filer som en testklass eller testmetod kräver för att kunna köras. Du kan ange filer eller mappar som ska kopieras till installationsmappen och eventuellt ange målväg i förhållande till installationsmappen.

Beskrivning

Används för rapportering, ger detta attribut en beskrivning av testmetoden. Otroligt är det här attributet endast tillgängligt på testmetoder och är inte tillgängligt på testklasser.

HostType

För testmetoder används det här attributet för att ange värden som enhetstestet ska köras in.

Prioritet

Det här attributet används inte av testmotorn, men kan användas, genom reflektion, med din egen testkod. Nyttjandet av detta attribut är tvivelaktigt.

Arbets objekt

Om du använder Team Foundation Server (TFS) kan du använda det här attributet på en testmetod för att ange det arbetsobjekt ID som tilldelats av TFS till det specifika enhetstestet.

CssIteration / CssProjectStructure

Dessa två attribut används i samband med TeamBuild och TestManagementService och låter dig ange en projekt iteration som testmetoden motsvarar.


Parameteriserad testning med DataSource-attributet

Microsofts enhetstestmotor stöder CSV-, XML- eller databasdatakällor för parametrerad testning. Detta är inte exakt sant parametrerad testning (se hur NUnit implementerar parametrerad testning) eftersom parametrarna inte överförs till enhetstestmetoden men måste extraheras från datakällan och skickas till den metod som testas. Möjligheten att ladda testdata till en DataTable från olika källor är dock till hjälp för körtestautomatisering.

CSV-datakälla

En textfiler med kommatecken kan användas för en datakälla:

Numerator, nämnare, ExpectedResult 10, 5, 2 20,5, 4 33, 3, 11 

och används i en testmetod:

[TestClass] public class DataSourceExamples public TestContext TestContext get; uppsättning;  [TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.CSV", "C: \\ temp \\ csvData.txt", "csvData # txt", DataAccessMethod.Sequential)] public void CsvDataSourceTest () int n = Convert.ToInt32 (TestContext.DataRow ["Numerator"]); int d = Convert.ToInt32 (TestContext.DataRow ["Denominator"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Det resulterar i följande utmatning:

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

Observera att testresultatfönstret inte visar parameterns körningar (kontrast till NUnit):

Parameteriserade testresultat

Det finns emellertid uppenbara fördelar att inte visa varje testkombination, särskilt för stora dataset.

XML-datakälla

Med en XML-fil som:

    

ett exempel på att använda en XML-datakälla för ett test är:

[TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.XML", "C: \\ temp \\ xmlData.xml", "Row", DataAccessMethod.Sequential)] offentligt tomrum XmlDataSourceTest () int n = Convert .ToInt32 (TestContext.DataRow [ "Täljare"]); int d = Convert.ToInt32 (TestContext.DataRow ["Denominator"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Observera att andra än datakällans attributparametrar, testkoden är densamma.

Databas Datakälla

En databas tabell kan också användas som datakälla. Givet ett bord som:

Databas tabell som datakälla

och data:

Databas Testdata

Ett exempel på testmetod som använder dessa data ser ut som:

[TestMethod] [DataSource ("System.Data.SqlClient", "Datakälla = INTERACX-HP; Initial Catalog = UnitTesting; Integrated Security = True"; "DivideTestData"; DataAccessMethod.Sequential)] public void XmlDataSourceTest = Convert.ToInt32 (TestContext.DataRow ["Numerator"]); int d = Convert.ToInt32 (TestContext.DataRow ["Denominator"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Återigen observera att testmetodkoden själv är densamma. Det enda vi har gjort här är att ändra DataSource-definitionen.

TestProperty Attribut

MSDN-dokumentationen för det här attributet illustrerar att deklarerar ett testprov-namnparametrar och sedan, genom att använda reflektion, förvärvar namnet och värdet. Detta verkar vara ett stumt sätt att skapa parametrerade tester. 

Dessutom påverkar koden, som beskrivs på Craig Anderas blogg, att använda TestProperty-attributet för att parametrera testinitieringsprocessen, inte TestContext.Properties-samlingen på Visual Studio 2008 eller Visual Studio 2012.