确定代码是否作为unit testing的一部分运行

我有一个unit testing(nUnit)。 如果通过unit testing运行,则调用堆栈中的许多图层将失败。

理想情况下,你会使用类似嘲笑的东西来设置这个方法依赖的对象,但这是第三方代码,我不能做很多工作。

我不想安装nUnit特定的方法 – 这里有太多的级别,它是一个糟糕的unit testing方式。

相反,我想要做的就是在调用堆栈中添加这样的内容

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

那么有关如何编写IsRunningInUnitTest的任何想法?

PS我完全知道,这不是伟大的devise,但我认为它比替代品更好。

我之前做过这件事 – 当我做到的时候,我不得不捂住鼻子,但是我做到了。 每次都有实用主义打败教条主义。 当然,如果有一个很好的方法,你可以重构,以避免它,这将是伟大的。

基本上我有一个“UnitTestDetector”类,它检查NUnit框架程序集是否加载到当前AppDomain中。 它只需要做一次,然后caching结果。 丑陋,但简单而有效。

以乔恩的想法,这是我想出了 –

 using System; using System.Reflection; /// <summary> /// 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. /// </summary> 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; } } } 

在后面stream下来,我们都是足够大的男孩,以承认我们正在做什么,我们可能不应该;)

改编自Ryan的回答。 这个是MSunit testing框架。

我需要这个的原因是因为我显示一个错误的MessageBox。 但是我的unit testing也testing了error handling代码,并且我不想在运行unit testing时popup一个MessageBox。

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

这是一个unit testing:

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

我使用了类似于tallseth的方法

这是可以很容易地修改为包含caching的基本代码。 另一个好主意是将一个setter添加到IsRunningInUnitTest并调用UnitTestDetector.IsRunningInUnitTest = false到您的项目主入口点以避免代码执行。

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

简化Ryan的解决scheme,您可以将以下静态属性添加到任何类:

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

也许有用,检查当前的ProcessName:

 public static bool UnitTestMode { get { string 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 } } 

而这个函数也应该由unittest来检查:

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

参考文献:
Matthew Watson在http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/11e68468-c95e-4c43-b02b-7045a52b407e/

在testing模式下,Assembly.GetEntryAssembly()似乎为空。

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

请注意,如果Assembly.GetEntryAssembly()为null,则Assembly.GetExecutingAssembly()不是。

该文档说:GetEntryAssembly方法可以返回null从非托pipe应用程序中加载托pipe程序集时。

用这个跳过的逻辑,在启动的时候没有连接debugging器就会禁用log4net中的所有TraceAppender。 这使unit testing即使在非debugging模式下运行,也可以login到Resharper结果窗口。

使用这个函数的方法要么在应用程序启动时,要么在启动testing装置时被调用。

它类似于Ryan的post,但是使用LINQ,放弃了System.Reflection的要求,不caching结果,并且是私人的防止(意外的)滥用。

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

在正在testing的项目中的某个地方:

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

在您的unit testing项目中的某个地方:

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

优雅,没有。 但是直接和快速。 AssemblyInitializer用于MS Test。 我希望其他testing框架具有等同的。

我最近不高兴有这个问题。 我用稍微不同的方式解决了这个问题。 首先,我不愿意假设nunit框架永远不会在testing环境之外加载; 我特别担心在他们的机器上运行应用程序的开发人员。 所以我走了调用堆栈。 其次,我能够假设testing代码永远不会针对发布二进制文件运行,所以我确信这个代码在发布系统中不存在。

 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().GetCustomAttributes(false)).Any(NunitAttribute); } private static IEnumerable<StackFrame> GetStackFrames(StackTrace callStack) { return callStack.GetFrames() ?? new StackFrame[0]; } private static bool NunitAttribute(object attr) { var type = attr.GetType(); if (type.FullName != null) { return type.FullName.StartsWith("nunit.framework", StringComparison.OrdinalIgnoreCase); } return false; } private enum Mode { Unknown, Test, Regular } 

奇迹般有效

 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; } 

考虑到你的代码通常运行在Windows窗体应用程序的主(gui)线程中,并且希望它在testing中运行时可以有所不同,那么可以检查

 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(); } 

我正在使用这个代码,我想在gui应用程序中fire and forgot ,但在unit testing中,我可能需要计算结果的断言,我不想混乱运行多个线程。

适用于MSTest。 它的好处是我的代码不需要检查testing框架本身,如果我真的需要在某个testing中的asynchronous行为,我可以设置我自己的SynchronizationContext。

请注意,这是不是一个可靠的方法来Determine if code is running as part of a unit test由于OP要求,因为代码可以在一个线程内运行,但在某些情况下,这可能是一个很好的解决scheme(也:如果我已经从后台线程运行,可能没有必要开始一个新的)。

有一个非常简单的解决scheme,当你正在testing一个类…

简单地给你正在testing这样的属性的类:

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

现在你的unit testing可以设置“thisIsUnitTest”布尔值为true,所以在你想跳过的代码中,添加:

  if (thisIsUnitTest) { return; } 

它比检查组件更容易,更快捷。 让我想起Ruby On Rails,看看你是否处于testing环境。