Déterminer si le code est en cours d’exécution dans le cadre d’un test unitaire

J’ai un test unitaire (nUnit). De nombreux calques dans la stack d’appels, une méthode échouera si elle est exécutée via un test unitaire.

Idéalement, vous utiliseriez quelque chose comme de la moquerie pour configurer l’object dont dépend cette méthode, mais il s’agit d’un code tiers et je ne peux pas le faire sans beaucoup de travail.

Je ne veux pas configurer de méthodes spécifiques à nUnit – il y a trop de niveaux ici et c’est une mauvaise façon de faire des tests unitaires.

Au lieu de cela, je voudrais append quelque chose comme ça au plus profond de la stack d’appels.

#IF DEBUG // Unit tests only included in debug build if (IsRunningInUnitTest) { // Do some setup to avoid error } #endif 

Donc, toutes les idées sur la façon d’écrire IsRunningInUnitTest?

PS Je suis pleinement conscient que ce n’est pas un design génial, mais je pense que c’est mieux que les alternatives.

Je l’ai déjà fait – je devais tenir mon nez pendant que je le faisais, mais je l’ai fait. Le pragmatisme bat à chaque fois le dogmatisme. Bien sûr, si vous pouvez vous refaire une bonne façon de l’éviter, ce serait formidable.

Au fond, j’avais une classe “UnitTestDetector” qui vérifiait si l’assembly du framework NUnit était chargé dans AppDomain actuel. Il suffit de le faire une fois, puis de mettre le résultat en cache. Moche, mais simple et efficace.

Prenant l’idée de Jon c’est ce que j’ai imaginé –

 using System; using System.Reflection; ///  /// Detect if we are running as part of a nUnit unit test. /// This is DIRTY and should only be used if absolutely necessary /// as its usually a sign of bad design. ///  static class UnitTestDetector { private static bool _runningFromNUnit = false; static UnitTestDetector() { foreach (Assembly assem in AppDomain.CurrentDomain.GetAssemblies()) { // Can't do something like this as it will load the nUnit assembly // if (assem == typeof(NUnit.Framework.Assert)) if (assem.FullName.ToLowerInvariant().StartsWith("nunit.framework")) { _runningFromNUnit = true; break; } } } public static bool IsRunningFromNUnit { get { return _runningFromNUnit; } } } 

Nous sums tous assez grands pour reconnaître quand nous faisons quelque chose que nous ne devrions probablement pas faire;)

Adapté de la réponse de Ryan. Celui-ci est pour le framework de test d’unité MS.

La raison pour laquelle j’en ai besoin est que je montre un MessageBox en cas d’erreur. Mais mes tests unitaires testent également le code de gestion des erreurs, et je ne veux pas qu’une MessageBox apparaisse lors de l’exécution des tests unitaires.

 ///  /// Detects if we are running inside a unit test. ///  public static class UnitTestDetector { static UnitTestDetector() { ssortingng testAssemblyName = "Microsoft.VisualStudio.QualityTools.UnitTestFramework"; UnitTestDetector.IsInUnitTest = AppDomain.CurrentDomain.GetAssemblies() .Any(a => a.FullName.StartsWith(testAssemblyName)); } public static bool IsInUnitTest { get; private set; } } 

Et voici un test unitaire pour cela:

  [TestMethod] public void IsInUnitTest() { Assert.IsTrue(UnitTestDetector.IsInUnitTest, "Should detect that we are running inside a unit test."); // lol } 

J’utilise une approche similaire à celle de tallseth

C’est le code de base qui pourrait être facilement modifié pour inclure la mise en cache. Une autre bonne idée serait d’append un setter à IsRunningInUnitTest et d’appeler UnitTestDetector.IsRunningInUnitTest = false au point d’entrée principal de vos projets pour éviter l’exécution du code.

 public static class UnitTestDetector { public static readonly HashSet UnitTestAtsortingbutes = new HashSet { "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAtsortingbute", "NUnit.Framework.TestFixtureAtsortingbute", }; public static bool IsRunningInUnitTest { get { foreach (var f in new StackTrace().GetFrames()) if (f.GetMethod().DeclaringType.GetCustomAtsortingbutes(false).Any(x => UnitTestAtsortingbutes.Contains(x.GetType().FullName))) return true; return false; } } } 

En simplifiant la solution de Ryan, vous pouvez simplement append la propriété statique suivante à n’importe quelle classe:

  public static readonly bool IsRunningFromNUnit = AppDomain.CurrentDomain.GetAssemblies().Any( a => a.FullName.ToLowerInvariant().StartsWith("nunit.framework")); 

En mode test, Assembly.GetEntryAssembly () semble être nul.

 #IF DEBUG // Unit tests only included in debug build if (Assembly.GetEntryAssembly() == null) { // Do some setup to avoid error } #endif 

Notez que si Assembly.GetEntryAssembly () est null, Assembly.GetExecutingAssembly () ne l’est pas.

La documentation indique: La méthode GetEntryAssembly peut renvoyer null lorsqu’un assembly géré a été chargé à partir d’une application non gérée.

Peut-être utile, en vérifiant le ProcessName actuel:

 public static bool UnitTestMode { get { ssortingng processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; return processName == "VSTestHost" || processName.StartsWith("vstest.executionengine") //it can be vstest.executionengine.x86 or vstest.executionengine.x86.clr20 || processName.StartsWith("QTAgent"); //QTAgent32 or QTAgent32_35 } } 

Et cette fonction devrait également être vérifiée par unittest:

 [TestClass] public class TestUnittestRunning { [TestMethod] public void UnitTestRunningTest() { Assert.IsTrue(MyTools.UnitTestMode); } } 

Les références:
Matthew Watson dans http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/11e68468-c95e-4c43-b02b-7045a52b407e/

Quelque part dans le projet testé:

 public static class Startup { public static bool IsRunningInUnitTest { get; set; } } 

Quelque part dans votre projet de test unitaire:

 [TestClass] public static class AssemblyInitializer { [AssemblyInitialize] public static void Initialize(TestContext context) { Startup.IsRunningInUnitTest = true; } } 

Elégant, non. Mais simple et rapide. AssemblyInitializer est pour MS Test. Je m’attendrais à ce que d’autres frameworks de test aient des équivalents.

Je l’utilise uniquement pour ignorer la logique qui désactive tous les TraceAppenders dans log4net lors du démarrage quand aucun débogueur n’est connecté. Cela permet aux tests unitaires de se connecter à la fenêtre de résultats Resharper même en mode non-débogage.

La méthode qui utilise cette fonction est appelée au démarrage de l’application ou lors du démarrage d’un test.

Il est similaire à la publication de Ryan, mais utilise LINQ, supprime l’exigence System.Reflection, ne met pas le résultat en cache et est privé pour empêcher toute utilisation abusive (accidentelle).

  private static bool IsNUnitRunning() { return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.ToLowerInvariant().StartsWith("nunit.framework")); } 

J’étais malheureux d’avoir ce problème récemment. Je l’ai résolu d’une manière légèrement différente. Premièrement, je ne voulais pas faire l’hypothèse que le framework nunit ne serait jamais chargé en dehors d’un environnement de test; J’étais particulièrement inquiet au sujet des développeurs exécutant l’application sur leurs machines. Donc, j’ai marché la stack d’appels à la place. Deuxièmement, j’ai été en mesure de faire l’hypothèse que le code de test ne serait jamais exécuté sur des binarys de version, donc je me suis assuré que ce code n’existait pas dans un système de publication.

 internal abstract class TestModeDetector { internal abstract bool RunningInUnitTest(); internal static TestModeDetector GetInstance() { #if DEBUG return new DebugImplementation(); #else return new ReleaseImplementation(); #endif } private class ReleaseImplementation : TestModeDetector { internal override bool RunningInUnitTest() { return false; } } private class DebugImplementation : TestModeDetector { private Mode mode_; internal override bool RunningInUnitTest() { if (mode_ == Mode.Unknown) { mode_ = DetectMode(); } return mode_ == Mode.Test; } private Mode DetectMode() { return HasUnitTestInStack(new StackTrace()) ? Mode.Test : Mode.Regular; } private static bool HasUnitTestInStack(StackTrace callStack) { return GetStackFrames(callStack).SelectMany(stackFrame => stackFrame.GetMethod().GetCustomAtsortingbutes(false)).Any(NunitAtsortingbute); } private static IEnumerable GetStackFrames(StackTrace callStack) { return callStack.GetFrames() ?? new StackFrame[0]; } private static bool NunitAtsortingbute(object attr) { var type = attr.GetType(); if (type.FullName != null) { return type.FullName.StartsWith("nunit.framework", SsortingngComparison.OrdinalIgnoreCase); } return false; } private enum Mode { Unknown, Test, Regular } 

fonctionne comme un charme

 if (AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.ToLowerInvariant().StartsWith("nunit.framework")) != null) { fileName = @"C:\Users\blabla\xxx.txt"; } else { var sfd = new SaveFileDialog { ... }; var dialogResult = sfd.ShowDialog(); if (dialogResult != DialogResult.OK) return; fileName = sfd.FileName; } 

.

Considérant que votre code est normalement exécuté dans le thread principal (gui) d’une application Windows Forms et que vous voulez qu’il se comporte différemment lors de l’exécution d’un test, vous pouvez vérifier

 if (SynchronizationContext.Current == null) { // code running in a background thread or from within a unit test DoSomething(); } else { // code running in the main thread or any other thread where // a SynchronizationContext has been set with // SynchronizationContext.SetSynchronizationContext(synchronizationContext); DoSomethingAsync(); } 

J’utilise ceci pour le code que je veux fire and forgot dans une application de gui mais dans les tests unitaires je pourrais avoir besoin du résultat calculé pour une assertion et je ne veux pas gâcher avec plusieurs threads en cours d’exécution.

Fonctionne pour MSTest. L’avantage est que mon code n’a pas besoin de vérifier le framework de test lui-même et si j’ai vraiment besoin du comportement asynchrone dans un certain test, je peux définir mon propre SynchronizationContext.

Sachez que ce n’est pas une méthode fiable pour Determine if code is running as part of a unit test demandé par OP, car le code peut être exécuté dans un thread, mais pour certains scénarios, cela peut être une bonne solution. en cours d’exécution à partir d’un thread d’arrière-plan, il peut ne pas être nécessaire d’en démarrer un nouveau.

Avoir une référence au framework nunit ne signifie pas que le test est en cours d’exécution. Par exemple, dans Unity lorsque vous activez les tests de mode de lecture, les références non-unifiées sont ajoutées au projet. Et lorsque vous exécutez un jeu, les références existent, donc UnitTestDetector ne fonctionnerait pas correctement.

Au lieu de vérifier l’assemblage de la nunité, nous pouvons demander à nitit api de vérifier si le code est en cours d’exécution ou non.

 using NUnit.Framework; // ... if (TestContext.CurrentContext != null) { // nunit test detected // Do some setup to avoid error } 

Application.Current est nul lorsqu’il est exécuté sous le testeur d’unité. Au moins pour mon application WPF utilisant le testeur MS Unit. C’est un test facile à faire si nécessaire. En outre, quelque chose à garder à l’esprit lorsque vous utilisez Application.Current dans votre code.

Il existe une solution très simple également lorsque vous testez une classe …

Donnez simplement à la classe que vous testez une propriété comme ceci:

 // For testing purposes to avoid running certain code in unit tests. public bool thisIsUnitTest { get; set; } 

Maintenant, votre test unitaire peut définir le booléen “thisIsUnitTest” sur true. Dans le code que vous souhaitez ignorer, ajoutez:

  if (thisIsUnitTest) { return; } 

C’est plus facile et rapide que d’inspecter les assemblages. Cela me rappelle Ruby On Rails où vous chercheriez à voir si vous êtes dans l’environnement TEST.