Enhetsprovning Succinctly Hur fungerar Unit Testing?

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

En enhetstestmotor på ett reflekterande språk (som något .NET-språk) har tre delar:

  • Laddar enheter som innehåller testen.
  • Använda reflektion för att hitta testmetoderna.
  • Inrätta metoderna och validera resultaten.

Den här artikeln innehåller kodexempel på hur det fungerar, och sätter ihop en enkel testmotor. Om du inte är intresserad av undersökningen av enhetstestmotorerna, glöm inte att hoppa över den här artikeln.

Koden här antar att vi skriver en testmotor mot Visual Studio-enhetstestattributen som definieras i Microsoft.VisualStudio.QualityTools.UnitTestFramework-sammansättningen. Andra testmaskiner kan använda andra attribut för samma ändamål.

Lastar monteringar

Arkitektoniskt borde din enhetstest antingen ligga i en separat montering från koden som testas, eller åtminstone bör endast ingå i enheten om den är sammanställd i "Debug" -läget. Fördelen med att sätta enhetstesterna i en separat enhet är att du också kan prova den icke-felsökningsoptimerade, optimerade produktionsversionen av koden.

Det sagt är det första steget att ladda enheten:

statisk bool LoadAssembly (string assemblyFilename, out Assembly assy, ​​utsträngsproblem) bool ok = true; issue = String.Empty; assy = null; försök assy = Assembly.LoadFile (assemblyFilename);  fångst (Undantag ex) issue = "Fel vid lastning av montering:" + ex.Message; ok = false;  återgå ok; 

Observera att professionella enhetstestmotorer laddar aggregat i en separat applikationsdomän så att aggregatet kan lossas eller laddas om utan att starta om testmotorn på nytt. Detta gör det också möjligt att ommontera enhetstestmonteringen och de beroende enheterna utan att först stänga av testmotorn.

Använda reflektion för att hitta enhetstestmetoder

Nästa steg är att reflektera över enheten för att identifiera de klasser som är betecknade som en "test fixture" och inom dessa klasser för att identifiera testmetoderna. En grundläggande uppsättning av fyra metoder stöder kraven på minsta enhetstestmotor, upptäckten av testfixturer, testmetoder och attribut för undantagshantering:

///  /// Returnerar en lista över klasser i den försedda enheten som har ett "TestClass" -attribut. ///  statisk IEnumerbar GetTestFixtures (Assembly Assy) return assy.GetTypes (). Var (t => t.GetCustomAttributes (typof (TestClassAttribute), false) .Length == 1);  ///  /// Returnerar en lista över metoder i testfästet som är dekorerat med attributet "TestMethod". ///  statisk IEnumerbar GetTestMethods (Typ testFixture) return testFixture.GetMethods (). Var (m => m.GetCustomAttributes (typof (TestMethodAttribute), false) .Length == 1);  ///  /// Returnerar en lista med specifika attribut som kan dekorera metoden. ///  statisk IEnumerbar GetMethodAttributes(MethodInfo metod) return method.GetCustomAttributes (typof (AttrType), false) .Cast();  ///  /// Returneras sant om metoden är dekorerad med ett attribut "ExpectedException" medan undantagstyp är det förväntade undantaget. ///  statisk bool IsExpectedException (MethodInfo-metod, Exception expectedException) Typ expectedExceptionType = expectedException.GetType (); returnera GetMethodAttributes(metod). Var (attr => attr.ExceptionType == expectedExceptionType) .Count ()! = 0; 

Inkalla metoder

När denna information har sammanställts, påminner motorn testmetoderna i ett provtagningsblock (vi vill inte att testmaskinen själv kraschar):

statisk tomrum RunTests (Typ testFixture, Action resultat) IEnumerable testMethods = GetTestMethods (testFixture); om (testMethods.Count () == 0) // Gör ingenting om det inte finns några testmetoder. lämna tillbaka;  objektet inst = Aktivator.CreateInstance (testFixture); foreach (MethodInfo mi i testMethods) bool pass = false; försök // Testmetoderna har inga parametrar. mi.Invoke (inst, null); pass = true;  fångst (Undantag ex) pass = IsExpectedException (mi, ex.InnerException);  äntligen result (testfix). "+". "+ mi.Name +": "+ (pass?" Pass ":" Misslyckas ")); 

Slutligen kan vi sätta denna kod ihop i en enkel konsolansökan som tar enhetstestaggregatet, eftersom parametern resulterar i en användbar men enkel motor:

använder system; använder System.Collections.Generic; använder system.IO; använder system.Linq; Använda System.Reflection; använder System.Text; använder Microsoft.VisualStudio.TestTools.UnitTesting; namespace SimpleUnitTestEngine class Program static void Main (sträng [] args) string issue; om (! VerifyArgs (args, out issue)) Console.WriteLine (issue); lämna tillbaka;  Sammansättning Assy; om (! LoadAssembly (args [0], ut assy, ​​ut issue)) Console.WriteLine (issue); lämna tillbaka;  IEnumerable testFixtures = GetTestFixtures (assy); foreach (Typ testFixture i testFixtures) RunTests (testFixture, t => Console.WriteLine (t));  statisk bool VerifyArgs (sträng [] args, utsträngsproblem) bool ok = true; issue = String.Empty; om (args.Length! = 1) issue = "Användning: SimpleUnitTestEngine "; ok = false; else string assemblyFilename = args [0]; om (! File.Exists (assemblyFilename)) issue =" Filnamnet "" + args [0] + "" existerar inte. " = false; returnera ok; ... resten av koden ... 

Resultatet av att köra den här enkla testmotorn visas i ett konsolfönster, till exempel:

Våra enkla testmotorkonsolresultat