Unitatea de testare succintă Cum funcționează unitatea de testare?

Acesta este un extras din Cartea electronică de testare a unității, de Marc Clifton, oferită cu amabilitate de Syncfusion.

Un motor de test unitate într-un limbaj reflectorizant (cum ar fi orice limbă .NET) are trei părți:

  • Încărcarea ansamblurilor care conțin încercările.
  • Utilizarea reflecției pentru a găsi metodele de testare.
  • Invocând metodele și validând rezultatele.

Acest articol oferă exemple de coduri despre modul în care funcționează acest lucru, punând împreună un motor simplu de testare a unității. Dacă nu sunteți interesat de ancheta sub capotă a motoarelor de test unitate, nu ezitați să omiteți acest articol.

Codul de aici presupune că scriem un motor de test împotriva atributelor de testare ale unității Visual Studio definite în ansamblul Microsoft.VisualStudio.QualityTools.UnitTestFramework. Alte motoare de testare a unităților pot utiliza alte atribute pentru aceleași scopuri.

Încărcarea ansamblurilor

Din punct de vedere arhitectural, testele de unitate ar trebui fie să se afle într-un ansamblu separat de codul testat, fie cel puțin, să fie incluse în ansamblu numai dacă sunt compilate în modul "Debug". Beneficiul introducerii testelor într-un ansamblu separat este că puteți, de asemenea, să testați versiunea de producție optimizată, care nu se depanează, de cod.

Acestea fiind spuse, primul pas este de a încărca ansamblul:

static bool LoadAssembly (ansamblul șirFilename, out Assy asamblare, șir de șir) bool ok = true; issue = String.Empty; assy = null; încercați assy = Assembly.LoadFile (assemblyFilename);  captură (Excepție ex) issue = "Eroare la încărcarea ansamblului:" + ex.Message; ok = false;  retur ok; 

Rețineți că motoarele de testare a unităților profesionale încarcă ansamblurile într-un domeniu de aplicare separat, astfel încât ansamblul să poată fi descărcat sau reîncărcat fără a reporni motorul de testare al unității. Acest lucru permite, de asemenea, recompilarea ansamblului de testare a unității și a ansamblurilor dependente fără a opri mai întâi motorul de testare a unității.

Utilizarea reflecției pentru a găsi metode de testare a unității

Următorul pas este să se reflecte asupra ansamblului pentru a identifica clasele care sunt desemnate ca "elemente de testare" și în cadrul acelor clase pentru a identifica metodele de testare. Un set de bază de patru metode suportă cerințele minime pentru unitatea de testare a unităților, descoperirea elementelor de testare, metodele de testare și atributele de tratare a excepțiilor:

///  /// Returnează o listă de clase din ansamblul furnizat care are un atribut "TestClass". ///  static IEnumerabile GetTestFixtures (assy assembla) return assy.GetTypes () unde (t => t.GetCustomAttributes (typeof (TestClassAttribute), false) .Lungth == 1);  ///  /// Returnează o listă de metode din dispozitivul de testare care sunt decorate cu atributul "TestMethod". ///  static IEnumerabile GetTestMethods (Type testFixture) returnează testFixture.GetMethods () unde (m => m.GetCustomAttributes (typeof (TestMethodAttribute), false) .Lungth == 1);  ///  /// Returnează o listă de atribute specifice care ar putea fi decorarea metodei. ///  static IEnumerabile GetMethodAttributes(Metoda MethodInfo) return method.GetCustomAttributes (typeof (AttrType), false) .Cast();  ///  /// Returnează true dacă metoda este decorată cu un atribut "ExpectedException", în timp ce excepția tip este excepția așteptată. ///  static bool IsExpectedException (Metoda MethodInfo, excepție ExcepțieExcepție) Type expectedExceptionType = expectedException.GetType (); returnați GetMethodAttributes(metodă). În cazul în care (attr => attr.ExceptionType == expectedExceptionType) .Count ()! = 0; 

Invocând metodele

Odată ce aceste informații au fost compilate, motorul invocă metodele de testare în cadrul unui bloc de încercare (nu vrem ca motorul de testare al unității să se prăbușească):

statice void RunTests (Tip testFixture, Acțiune rezultat) IEnumerabile testMethods = GetTestMethods (testFixture); dacă (testMethods.Count () == 0) // Nu faceți nimic dacă nu există metode de testare. întoarcere;  obiect inst = Activator.CreateInstance (test fixture); foreach (MetodaInfo mi în testMethods) bool pass = false; încercați // Metodele de testare nu au parametri. mi.Invoke (inst, null); pass = true;  captură (Excepție ex) pass = IsExpectedException (mi, ex.InnerException);  în cele din urmă rezultat (testFixture.Name + "." + mi.Name + ":" + (treci "Pass": "Fail")); 

În cele din urmă, putem pune împreună acest cod într-o aplicație simplă a consolei care ia un ansamblu de testare a unității ca rezultat al parametrilor într-un motor utilizabil, dar simplu:

utilizând Sistemul; folosind System.Collections.Generic; utilizând System.IO; folosind System.Linq; utilizând System.Reflection; folosind System.Text; folosind Microsoft.VisualStudio.TestTools.UnitTesting; namespace SimpleUnitTestEngine program de clasă static void Principal (șir [] args) șir de chei; dacă (! VerifyArgs (args, out issue)) Console.WriteLine (problema); întoarcere;  Assy de asamblare; dacă (! LoadAssembly (args [0], out assy, ​​afară)) Console.WriteLine (issue); întoarcere;  IEnumerabile testFixtures = GetTestFixtures (assy); foreach (Testul de tip în testFixturi) RunTests (testFixture, t => Console.WriteLine (t));  boot static VerifyArgs (string [] args, șir de șir) bool ok = true; issue = String.Empty; dacă (args.Length! = 1) issue = "Utilizare: SimpleUnitTestEngine "alt = string fileFilename = args [0]; if (! File.Exists (assemblyFilename)) issue =" Numele fișierului "+ args [0] +" = false; return ok; ... restul codului ... 

Rezultatul executării acestui motor simplu de testare este afișat într-o fereastră a consolei, de exemplu:

Rezultatele consolei noastre de testare simple
Cod