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:
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.
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.
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 IEnumerbarGetTestFixtures (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 IEnumerbarGetTestMethods (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 IEnumerbarGetMethodAttributes (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;
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, Actionresultat) 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; IEnumerabletestFixtures = 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