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:
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
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).
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:
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.
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:
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:
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.
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:
[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.