Acesta este un extras din Cartea electronică de testare a unității, de Marc Clifton, oferită cu amabilitate de Syncfusion.
Un test de unitate este compus din două lucruri:
O clasă care reprezintă dispozitivul de testare.
Metodele din clasă reprezintă testele unității.
Visual Studio va crea automat un stub pentru un proiect de testare, de unde vom începe.
Crearea unui proiect de testare a unității în Visual Studio
Testele de unitate sunt de obicei plasate într-un proiect separat (rezultând într-o asamblare distinctă) din codul de aplicare. În Visual Studio 2008 sau 2012, puteți crea un proiect pentru unitatea de testare prin clic dreapta pe soluție și selectând Adăuga urmat de Proiect nou din meniul pop-up:
Adăugarea unui nou proiect
Din caseta de dialog care apare, selectați a Test proiect:
Proiectul noului test VS2008 VS2012 Proiect nou de testare
Visual Studio 2008 va crea un fișier stub, "UnitTest1.cs" (dacă ați selectat limba C #), cu o varietate de comentarii utile în stub. Visual Studio 2012 creează un stub mult mai terser:
utilizând Sistemul; folosind Microsoft.VisualStudio.TestTools.UnitTesting; namespace VS2012UnitTestProject1 [TestClass] clasă publică UnitTest1 [TestMethod] public void TestMethod1 ()
Pentru utilizatorii Visual Studio 2008
Visual Studio 2008 va crea, de asemenea, o clasă TestContext - aceasta nu mai există în VS2012 și o vom ignora - utilizați stubul anterior din VS2012.
De asemenea, ștergeți fișierul "ManualTest1.mht", altfel vi se va solicita să selectați rezultatele testelor și să introduceți manual notele de testare.
Testele de testare
Observați că clasa este decorată cu atributul TestClass. Aceasta definește dispozitivul de testare - o colecție de metode de testare.
Metode de testare
Observați că metoda este decorată cu atributul TestMethod. Aceasta definește o metodă pe care se va executa dispozitivul de testare.
Clasa Assert
Clasa assert definește următoarele metode statice care pot fi utilizate pentru a verifica calculul unei metode:
AreEqual / AreNotEqual
AreSame / AreNotSame
IsTrue / IsFalse
Isnull / IsNotNull
IsInstanceOfType / IsNotInstanceOfType
Multe dintre aceste afirmații sunt supraîncărcate și este recomandat să consultați documentația completă pe care Microsoft o furnizează.
Fundamentele de a face o afirmație
Rețineți că următoarele exemple utilizează VS2008.
Aserțiunile sunt coloana vertebrală a fiecărui test. Există o varietate de afirmații cu privire la rezultatele unui test. În primul rând, vom scrie o afirmație simplă care spune că "unul este egal cu unul", cu alte cuvinte, un truism:
[TestClass] clasa publica UnitTest1 [TestMethod] public void TestMethod1 () Assert.IsTrue (1 == 1);
Executați testul, care ar trebui să aibă drept rezultat "Passed":
Simpla afirmație
AreEqual / AreNotEqual
Metodele AreEqual și AreNotEqual compară:
obiecte
duble
simplu
siruri de caractere
datele introduse
Acestea iau forma comparării așteptatului (primul parametru) cu valoarea reală (al doilea parametru). În ceea ce privește valorile unice și duble, pot fi specificate "cu o anumită precizie". În cele din urmă, toate suprasarcini au opțiunea de a afișa un mesaj (opțional formatat) dacă afirmația nu reușește.
În ceea ce privește egalitatea de obiecte, această metodă compară dacă instanțele sunt identice:
clasa publică AnObject [TestMethod] public void ObjectEqualityTest () AnObject object1 = nou AnObject (); Obiect AnObject2 = AnObject nou (); Assert.AreNotEqual (obiect1, obiect2);
Testul precedent trece, deoarece obiectul1 și obiectul2 nu sunt egale. Cu toate acestea, dacă clasa înlocuiește metoda Equals, atunci egalitatea se bazează pe comparația făcută prin metoda Equals implementată în clasă. De exemplu:
clasa publică AnObject public int SomeValue get; a stabilit; public override bool Egal (obiect obj) returnează SomeValue == ((AnObject) obj) .SomeValue; [TestMethod] public void ObjectEqualityTest () AnObject obiect1 = nou AnObject () SomeValue = 1; Obiect AnObject2 = nou AnObject () SomeValue = 1; Assert.AreEqual (obiect1, obiect2);
AreSame / AreNotSame
Aceste două metode verifică faptul că instanțe sunt aceleași (sau nu). De exemplu:
[TestMethod] void public SamenessTest () AnObject object1 = nou AnObject () SomeValue = 1; Obiect AnObject2 = nou AnObject () SomeValue = 1; Assert.AreNotSame (obiect1, obiect2);
Chiar dacă clasa AnObject suprascrie operatorul Equals, testul precedent trece din cauză că instanțe din cele două obiecte nu sunt aceleași.
IsTrue / IsFalse
Aceste două metode vă permit să testați adevărul unei comparații de valoare. Din perspectiva lizibilității, de obicei sunt utilizate metodele IsTrue și IsFalse valoare comparații, în timp ce AreEqual și AreSame sunt de obicei folosite pentru a compara instanțe (obiecte).
De exemplu:
[TestMethod] void public IsTrueTest () AnObject object1 = nou AnObject () SomeValue = 1; Assert.IsTrue (object1.SomeValue == 1);
Aceasta verifică valoarea unei proprietăți.
Isnull / IsNotNull
Aceste două teste verifică dacă un obiect este nul sau nu:
Aceste două metode verifică faptul că un obiect este o instanță de tip specific (sau nu). De exemplu:
clasa publică AnotherObject [TestMethod] public void TypeOfTest () AnObject object1 = nou AnObject (); Assert.IsNotInstanceOfType (obiect1, typeof (AnotherObject));
neconcludent
Metoda Assert.Inconclusive poate fi utilizată pentru a specifica că fie testul, fie funcționalitatea din spatele testului nu au fost încă implementate și, prin urmare, testul este neconcludent.
Ce se întâmplă când o afirmație eșuează?
În ceea ce privește testarea unității Visual Studio, atunci când o afirmație nu reușește, metoda Assert aruncă o AssertFailedException. Această excepție nu ar trebui să fie tratată niciodată de codul dvs. de testare.
Alte clase de afirmare
Există două alte clase de afirmații:
CollectionAssert
StringAssert
După cum sugerează numele lor, aceste afirmații operează pe colecții și, respectiv, șiruri de caractere.
Colecții
Aceste metode sunt implementate în clasa Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert. Rețineți că parametrul de colectare din aceste metode așteaptă ca colecția să pună în aplicare ICollection (contrastul cu NUnit, care se așteaptă să fie numit IEnumerable).
AllItemsAreInstanceOfType
Această afirmație verifică faptul că obiectele dintr-o colecție sunt de același tip, care includ tipuri derivate de tipul așteptat, așa cum este ilustrat aici:
clasa publica A public class B: A [TestClass] clasa publica CollectionTests [TestMethod] public void InstancesOfTypeTest () Lista puncte = lista noua () ); Listă
AllItemsAreNotNull
Această afirmație verifică faptul că obiectele din colecție nu sunt nule.
AllItemsAreUnique
Acest test asigură faptul că obiectele dintr-o colecție sunt unice. Dacă se compară structurile:
[TestMethod] public void AreUniqueTest () List points = lista nouă () new Point (1, 2), new Point (1, 2); CollectionAssert.AllItemsAreUnique (puncte);
structurile sunt comparate prin valoare, nu prin exemplu - testul precedent nu reușește. Cu toate acestea, chiar dacă clasa înlocuiește metoda Equals:
clasa publică AnObject public int SomeValue get; a stabilit; public override bool Egal (obiect obj) returnează SomeValue == ((AnObject) obj) .SomeValue;
acest test trece:
[TestMethod] public void AreUniqueObjectsTest () Listă articole = listă nouă() new AnObject () SomeValue = 1, noul AnObject () SomeValue = 1; CollectionAssert.AllItemsAreUnique (articole);
AreEqual / AreNotEqual
Aceste teste afirmă că două colecții sunt egale. Metodele includ suprasarcini care vă permit să furnizați o metodă de comparare. Dacă un obiect suprascrie metoda Equals, acea metodă va fi utilizată pentru a determina egalitatea. De exemplu:
[TestMethod] public void AreEqualTest () Listă itemList1 = Listă nouă() new AnObject () SomeValue = 1, noul AnObject () SomeValue = 2; Listă itemList2 = Listă nouă() new AnObject () SomeValue = 1, noul AnObject () SomeValue = 2; CollectionAssert.AreEqual (itemList1, itemList2);
Aceste două colecții sunt egale deoarece clasa AnObject suprascrie metoda Equals (a se vedea exemplul anterior).
Rețineți că pentru a transmite afirmația, listele trebuie să aibă aceeași lungime și sunt considerate egale dacă listele sunt identice, cu excepția unei ordini diferite. Comparați acest lucru cu afirmația AreEquivalent descrisă în continuare.
AreEquivalent / AreNotEquivalent
Această afirmație compară două liste și consideră că listele de elemente egale sunt echivalente, indiferent de ordin. Din păcate, se pare că există o eroare în implementare, deoarece acest test nu reușește:
[TestMethod] public void AreEqualTest () Listă itemList1 = Listă nouă() new AnObject () SomeValue = 1, noul AnObject () SomeValue = 2; Listă itemList2 = Listă nouă() new AnObject () SomeValue = 2, noul AnObject () SomeValue = 1; CollectionAssert.AreEquivalent (itemList1, itemList2);
Visual Studio's AreEquivalent Bug
cu mesajul de eroare:
CollectionAssert.AreEquivalent a eșuat. Colecția așteptată conține 1 eveniment (e) de. Colecția actuală conține 0 apariții (incidențe).
În timp ce implementarea de către NUnit a acestei afirmații trece:
Nivelele echivalente ale NUnit funcționează corect
Conține / DoesNotContain
Această afirmație verifică faptul că un obiect este conținut într-o colecție:
[TestMethod] public void ContainsTest () Listă itemList = Listă nouă() new AnObject () SomeValue = 1, noul AnObject () SomeValue = 2; CollectionAssert.Contains (itemList, noul AnObject () SomeValue = 1);
utilizând metoda Equals (dacă este suprascrisă) pentru a efectua testul de egalitate.
IsSubsetOf / IsNotSubsetOf
Această afirmație verifică faptul că primul parametru (subsetul) este conținut în colecția celui de-al doilea parametru (superset).
Rețineți că testul pentru subset nu testează ordinea sau secvența - pur și simplu testează dacă elementele din lista de subseturi sunt conținute în superset.
String Assertions
Aceste metode sunt implementate în clasa Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert:
Conține
Meciuri / DoesNotMatch
StartsWith / EndsWith
Acestea sunt discutate în continuare.
Conține
Metoda Contains afirmă că submultimea (rețineți că acesta este al doilea parametru) este conținută în șir (primul parametru). De exemplu, acest test trece:
[TestClass] clasa publică StringTests [TestMethod] void public ContainsTest () string subset = "abc"; string superset = "123abc456"; StringAssert.Contains (superset, subset);
Meciuri / DoesNotMatch
Această metodă afirmă că șirul (primul parametru) se potrivește cu modelul regex furnizat în al doilea parametru.
StartsWith / EndsWith
Această metodă afirmă că șirul (primul parametru) începe sau se termină cu un alt șir (al doilea parametru).
excepţii
Excepțiile pot fi testate fără scrierea blocurilor try-catch în jurul metodei de testare. De exemplu, în timp ce ați putea scrie acest lucru:
[TestMethod] public void CatchingExceptionsTest () încercați Divide (5, 0); catch (ArgumentOutOfRangeException) // Acceptați cu tăcere excepția ca valabilă.
Este mult mai ușor să se folosească atributul ExpectedException pe metoda de testare:
Există câteva atribute suplimentare care sunt utile pentru rularea unei suite de teste, precum și teste individuale care îmbunătățesc reutilizarea și lizibilitatea bazei de cod de testare a unității.
Unitatea de testare a unității Visual Studio oferă patru atribute de metode suplimentare:
ClassInitialize
ClassCleanup
TestInitialize
TestCleanup
Aceste atribute precede și urmăresc executarea tuturor testelor din cadrul dispozitivului (clasei), precum și înainte și după fiecare încercare din dispozitiv.
Rețineți că metodele decorate cu acest atribut trebuie să fie statice.
ClassInitialize
Dacă o metodă este decorată cu acest atribut, codul din metodă este executat înainte de a executa toate testele din dispozitiv. Rețineți că această metodă necesită un parametru TestContext.
Această metodă este utilă pentru a aloca resurse sau clase de instanțe pe care se bazează toate testele din dispozitiv. O importanță importantă cu resursele și obiectele create în timpul inițializării dispozitivului este că aceste resurse și obiecte ar trebui să fie considerate doar pentru citire. Nu este recomandabil ca testele să schimbe starea resurselor și a obiectelor de care depind alte teste. Acestea includ conexiuni la servicii precum baze de date și servicii web ale căror conexiuni ar putea fi introduse într-o stare nevalidă ca urmare a unei erori la un test, invalidând astfel toate celelalte teste. În plus, ordinea în care testele nu sunt garantate. Modificarea stării unei resurse și a unui obiect creat în inițializarea dispozitivului poate avea ca rezultat efecte secundare, în funcție de ordinea în care sunt executate testele.
ClassCleanup
O metodă decorată cu acest atribut este responsabilă pentru de-alocarea resurselor, închiderea conexiunilor etc. care au fost create în timpul inițializării clasei. Această metodă va fi executată întotdeauna după executarea testelor în cadrul dispozitivului, indiferent de succesul sau eșecul încercărilor.
TestInitialize
Similar cu atributul ClassInitialize, va fi executată o metodă decorată cu acest atribut pentru fiecare test înainte de a efectua testul. Unul dintre scopurile acestui atribut este să se asigure că resursele sau obiectele alocate de codul ClassInitialize sunt inițializate într-o stare cunoscută înainte de a executa fiecare test.
TestCleanup
Completând atributul TestInitialize, metodele decorate cu TestCleanup vor fi executate la finalizare din fiecare test.
Setarea și fluxul teardown
Următorul cod demonstrează curgerea dispozitivului de fixare și a încercării și stabilirea teardonului în raport cu testele reale:
[TestClass] clasa publica SetupTeardownFlow [ClassInitialize] static public void SetupFixture (Context TestContext) Debug.WriteLine ("Fixture Setup."); [ClassCleanup] public static void TeardownFixture () Debug.WriteLine ("Fixture Teardown."); [TestInitialize] public void SetupTest () Debug.WriteLine ("Setarea testului"); [TestCleanup] public void TeardownTest () Debug.WriteLine ("Test Teardown."); [TestMethod] public void TestA () Debug.WriteLine ("Test A."); [TestMethod] public void TestB () Debug.WriteLine ("Test B.");
Dacă rulați acest dispozitiv, rezultă următorul traseu de ieșire de depanare:
Configurare configurare. Configurarea testului. Test A. Test Teardown. Configurarea testului. Test B. Test Teardown. Înlocuire Teardown.
Așa cum este ilustrat în exemplul anterior, dispozitivul este inițializat - apoi pentru fiecare test, se execută setarea de testare și codul de teardon, urmat de teardown fixare la sfârșitul.
Atributele utilizate mai puțin frecvent
Următoarea secțiune descrie atributele utilizate mai puțin frecvent.
AssemblyInitialize / AssemblyCleanup
Metodele decorate cu acest atribut trebuie să fie statice și sunt executate atunci când ansamblul este încărcat. Acest lucru ridică întrebarea - ce se întâmplă dacă ansamblul are mai multe dispozitive de testare?
[TestClass] clasa publica Fixture1 [AssemblyInitialize] public static void AssemblyInit (contextul TestContext) // ... some operation [TestClass] public class Fixture2 [AssemblyInitialize] public static void AssemblyInit
Dacă încercați acest lucru, motorul de test nu execută niciun test de unitate, raportând:
"UTA013: UnitTestExamplesVS2008.Fixture2: Nu pot defini mai mult de o metodă cu atributul AssemblyInitialize în interiorul unui ansamblu."
Prin urmare, poate exista o singură metodă AssemblyInitialize și o singură metodă AssemblyCleanup pentru un ansamblu, indiferent de numărul de corpuri de testare din acel ansamblu. Prin urmare, se recomandă să nu fie introduse în clasă criterii reale care să definească aceste metode:
[TestClass] Adunarea publica a clasei publice [AssemblyInitialize] static public void AssemblySetup (contextul TestContext) Debug.WriteLine ("Initialize Assembly"); [AssemblyCleanup] public static void AdunareaTeardown () Debug.WriteLine ("Cleanup Assembly");
rezultând următoarea secvență de execuție:
Adunarea inițializată. Configurare configurare. Configurarea testului. Test A. Test Teardown. Configurarea testului. Test B. Test Teardown. Înlocuire Teardown. Curățarea ansamblului.
Rețineți ansamblul suplimentar de inițializare și de curățare a apelurilor.
Ignora
Această metodă poate decora metode specifice sau corpuri complete.
Ignorați o metodă de testare
Dacă acest atribut decorează o metodă de testare:
[TestMethod, Ignoră] public void TestA () Debug.WriteLine ("Test A.");
testul nu va fi rulat. Din păcate, panoul Rezultat al Visual Studio Test nu indică faptul că există teste ignorate în prezent:
Testele ignorate nu sunt afișate
Comparați acest lucru cu NUnit, care arată în mod clar testele ignorate:
NUnit arată teste ignorate
Afișajul NUnit marchează întregul arbore de test ca fiind "necunoscut" când una sau mai multe metode de testare sunt marcate ca "Ignoră".
Ignorați un dispozitiv de testare
Metodele întregului dispozitiv pot fi ignorate utilizând atributul Ignore la nivelul clasei:
[TestClass, Ignoră] clasa publică SetupTeardownFlow ... etc ...
Ștergerea cache-ului de test
Dacă adăugați atributul Ignore la o metodă, este posibil să observați că Visual Studio execută încă testul. Este necesar să ștergeți cache-ul de testare pentru Visual Studio pentru a ridica modificarea. O modalitate de a face acest lucru este să curățați soluția și să o reconstruiți.
Proprietar
Folosit în scopuri de raportare, acest atribut descrie persoana responsabilă de metoda de testare a unității.
DeploymentItem
Dacă testele unității sunt difuzate într-un folder separat de implementare, acest atribut poate fi folosit pentru a specifica fișierele pe care o clasă de testare sau o metodă de test necesită pentru a le rula. Aveți posibilitatea să specificați fișierele sau folderele pe care doriți să le copiați în folderul de implementare și să specificați opțional calea vizată în raport cu folderul de implementare.
Descriere
Utilizat pentru raportare, acest atribut oferă o descriere a metodei de testare. În mod ciudat, acest atribut este disponibil numai pe metodele de testare și nu este disponibil pe clase de test.
HostType
Pentru metodele de testare, acest atribut este folosit pentru a specifica gazda pe care va rula testul unității.
Prioritate
Acest atribut nu este utilizat de motorul de testare, dar poate fi folosit, prin reflecție, de propriul cod de testare. Utilitatea acestui atribut este discutabilă.
Element de lucru
Dacă utilizați Team Foundation Server (TFS), puteți utiliza acest atribut într-o metodă de testare pentru a specifica ID-ul de produs atribuit de TFS testului unității specifice.
CssIteration / CssProjectStructure
Aceste două atribute sunt utilizate în relație cu TeamBuild și TestManagementService și vă permit să specificați o iterație a proiectului la care metoda de testare corespunde.
Testarea parametrizată cu atributul DataSource
Unitatea de testare a unităților de testare Microsoft suportă surse de date CSV, XML sau baze de date pentru testarea parametrizată. Aceasta nu este o testare parametrizată exactă (vedeți cum NUnit implementează testarea parametrizată) deoarece parametrii nu sunt transmiși la metoda de testare a unității, ci trebuie extrași din sursa de date și trecuți la metoda testată. Cu toate acestea, capacitatea de a încărca date de testare într-un tabel de date dintr-o varietate de surse este utilă pentru a conduce automatizarea testelor.
Sursa de date CSV
Un fișier text cu valoare separată prin virgule poate fi utilizat pentru o sursă de date:
[TestClass] clasă publică DataSourceExamples public TestContext TestContext get; a stabilit; [TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.CSV", "C: \\ temp \\ csvData.txt", "csvData # txt", DataAccessMethod.Sequential)] void public CsvDataSourceTest 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);
Rezultă următoarele rezultate:
n = 10, d = 5, r = 2 n = 20, d = 5, r = 4 n = 33, d = 3, r =
Rețineți că fereastra cu rezultatele testului nu arată parametrii care rulează (contrastul cu NUnit):
Rezultate de testare parametrizate
Cu toate acestea, există avantaje evidente pentru a nu afișa fiecare combinație de test, în special pentru seturi de date mari.
Sursa de date XML
Având în vedere un fișier XML, cum ar fi:
un exemplu de utilizare a unei surse de date XML pentru un test de unitate este:
[TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.XML", "C: \\ temp \\ xmlData.xml", "Row", DataAccessMethod.Sequential)] void public XmlDataSourceTest () int n = .ToInt32 (TestContext.DataRow [ "Numărător"]); int d = Convert.ToInt32 (TestContext.DataRow ["Denominator"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);
Rețineți că, în afară de parametrii atributului sursă de date, codul de testare este același.
Sursa datelor bazei de date
O tabelă de baze de date poate fi, de asemenea, utilizată ca sursă de date. Având în vedere un tabel ca:
Tabelul de baze de date ca sursă de date
și date:
Date de testare a bazelor de date
O metodă de testare care utilizează aceste date arată astfel:
[TestMethod] [DataSource ("System.Data.SqlClient", "Sursa de date = INTERACX-HP; Catalog inițial = 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);
Din nou, observați că codul metodei de testare în sine este același - singurul lucru pe care l-am făcut aici este schimbarea definiției DataSource.
Atributul atributului
Documentația MSDN pentru acest atribut ilustrează declararea unei perechi de nume-valoare TestProperty și apoi, folosind reflecția, obținând numele și valoarea. Acest lucru pare să fie un mod obtuzabil de a crea teste parametrizate.
Mai mult, codul, descris pe blogul lui Craig Andera, de a folosi atributul TestProperty pentru a parametriza procesul de inițializare a testului, nu afectează colecția TestContext.Properties din Visual Studio 2008 sau Visual Studio 2012.