Unit Testing bondig hoe werkt unit-testing?

Dit is een uittreksel uit het Unit Testing Beknopte eBook, door Marc Clifton, vriendelijk geleverd door Syncfusion.

Een eenheidstestmotor in een reflectieve taal (zoals elke .NET-taal) bestaat uit drie delen:

  • Assemblies laden die de tests bevatten.
  • Reflectie gebruiken om de testmethoden te vinden.
  • Een beroep doen op de methoden en de resultaten valideren.

Dit artikel biedt codevoorbeelden van hoe dit werkt, het samenstellen van een eenvoudige eenheidstest-engine. Als u niet geïnteresseerd bent in het onderzoek onder de motorkap van testtestmotoren, kunt u dit artikel overslaan.

De code veronderstelt hierbij dat we een testengine schrijven tegen de testattributen van Visual Studio-eenheden die zijn gedefinieerd in de assembly Microsoft.VisualStudio.QualityTools.UnitTestFramework. Andere test-engines kunnen andere attributen gebruiken voor dezelfde doeleinden.

Assemblages plaatsen

In de architectuur moeten uw unit tests ofwel in een afzonderlijke assembly van de te testen code zitten, of op zijn minst alleen in de assembly worden opgenomen als deze in de "Debug" -modus is gecompileerd. Het voordeel van het in een afzonderlijke assembly plaatsen van de unit-tests is dat u ook de niet-debug-geoptimaliseerde productieversie van de code kunt testen.

Dat gezegd hebbende, is de eerste stap om de assembly te laden:

static bool LoadAssembly (string assemblyFilename, out Assembly assy, ​​out string issue) bool ok = true; issue = String.Empty; assy = null; probeer assy = Assembly.LoadFile (assemblyFilename);  catch (Exception ex) issue = "Fout bij laden van assembly:" + ex.Message; ok = false;  komt terug; 

Merk op dat professionele testlaboratoria assemblages in een afzonderlijk toepassingsdomein laden, zodat de assemblage kan worden leeggemaakt of opnieuw geladen zonder de unittestmotor opnieuw te starten. Hierdoor kunnen ook de eenheidstestsamenstelling en de afhankelijke samenstellingen opnieuw worden gecompileerd zonder eerst de testmotor van de eenheid uit te schakelen.

Reflectie gebruiken om eenheidstestmethoden te vinden

De volgende stap is om na te denken over de assembly om de klassen te identificeren die worden aangeduid als een "testopstelling" en binnen die klassen om de testmethoden te identificeren. Een basisset van vier methoden ondersteunt de minimum vereisten voor de unittestmotor, de ontdekking van testopstellingen, testmethoden en afhandelingsattributen voor uitzonderingen:

///  /// Retourneert een lijst met klassen in de opgegeven assembly met een kenmerk "TestClass". ///  static IEnumerable GetTestFixtures (Assembly assy) return assy.GetTypes (). Where (t => t.GetCustomAttributes (typeof (TestClassAttribute), false) .Length == 1);  ///  /// Retourneert een lijst met methoden in de testopstelling die zijn versierd met het kenmerk "TestMethod". ///  static IEnumerable GetTestMethods (Type testFixture) return testFixture.GetMethods (). Where (m => m.GetCustomAttributes (typeof (TestMethodAttribute), false) .Length == 1);  ///  /// Retourneert een lijst met specifieke kenmerken die de methode mogelijk versieren. ///  static IEnumerable GetMethodAttributes(MethodInfo-methode) retourmethode.GetCustomAttributes (typeof (AttrType), false) .Cast();  ///  /// Geeft als resultaat true als de methode is voorzien van het kenmerk 'ExpectedException', terwijl het exception-type de verwachte uitzondering is. ///  static bool IsExpectedException (MethodInfo-methode, Exception expectedException) Type expectedExceptionType = expectedException.GetType (); retourneer GetMethodAttributes(methode). Waar (attr => attr.ExceptionType == expectedExceptionType) .Count ()! = 0; 

Aanroepmethoden

Nadat deze informatie is gecompileerd, roept de engine de testmethoden aan binnen een try-catch-blok (we willen niet dat de eenheidstestmotor zelf crasht):

statische leegte RunTests (Type testfixure, actie resultaat) IEnumerable testMethods = GetTestMethods (testFixture); if (testMethods.Count () == 0) // Doe niets als er geen testmethoden zijn. terug te keren;  object inst = Activator.CreateInstance (testFixture); foreach (MethodInfo mi in testMethods) bool pass = false; probeer // Testmethoden hebben geen parameters. mi.Invoke (inst, nul); pass = true;  catch (Exception ex) pass = IsExpectedException (mi, ex.InnerException);  eindelijk result (testFixture.Name + "." + mi.Name + ":" + (pass? "Pass": "Fail")); 

Ten slotte kunnen we deze code samenvoegen in een eenvoudige consoletoepassing waarbij de eenheidstestassemblage als parameter resulteert in een bruikbare, maar eenvoudige engine:

systeem gebruiken; met behulp van System.Collections.Generic; met behulp van System.IO; met behulp van System.Linq; using System.Reflection; met behulp van System.Text; met behulp van Microsoft.VisualStudio.TestTools.UnitTesting; namespace SimpleUnitTestEngine class Program static void Main (string [] args) string issue; if (! VerifyArgs (args, out issue)) Console.WriteLine (probleem); terug te keren;  Vergadering assy; if (! LoadAssembly (args [0], out assy, ​​out issue)) Console.WriteLine (probleem); terug te keren;  IEnumerable testFixtures = GetTestFixtures (assy); foreach (Type testtest in testkwaliteit) RunTests (testFixture, t => Console.WriteLine (t));  static bool VerifyArgs (string [] args, out string issue) bool ok = true; issue = String.Empty; if (args.Length! = 1) issue = "Gebruik: SimpleUnitTestEngine "; ok = false; else string assemblyFilename = args [0]; if (! File.Exists (assemblyFilename)) issue =" De bestandsnaam "" + args [0] + "'bestaat niet."; ok = false; return ok; ... de rest van de code ... 

Het resultaat van het uitvoeren van deze eenvoudige testengine wordt weergegeven in een consolevenster, bijvoorbeeld:

Onze Simple Test Engine-consoleresultaten