代码之家  ›  专栏  ›  技术社区  ›  Ryan

确定代码是否作为单元测试的一部分运行

  •  92
  • Ryan  · 技术社区  · 14 年前

    我有单元测试。如果方法通过单元测试运行,那么调用堆栈中的许多层都将失败。

    理想情况下,您可以使用类似mocking的东西来设置这个方法所依赖的对象,但这是第三方代码,如果没有大量的工作,我就无法做到这一点。

    我不想设置nunit特定的方法——这里有太多的级别,这是一种糟糕的单元测试方法。

    相反,我想做的是在调用堆栈的深处添加类似这样的内容

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

    关于如何写isrunninginnittest有什么想法吗?

    P.S.我完全知道这不是一个伟大的设计,但是我 认为 比其他选择更好。

    16 回复  |  直到 6 年前
        1
  •  72
  •   Jon Skeet    14 年前

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

    基本上我有一个“UnitTestDetector”类,它检查nunit框架程序集是否加载在当前的AppDomain中。它只需要执行一次,然后缓存结果。丑陋,但简单有效。

        2
  •  66
  •   Nij    9 年前

    接受乔恩的想法这就是我想到的-

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

    在后面竖起烟斗,我们都是大得足以辨认出我们做了什么我们可能不该做的事情;)

        3
  •  48
  •   dan-gph    14 年前

    改编自Ryan的回答。这一个用于MS单元测试框架。

    我之所以需要这样做是因为我显示了一个关于错误的消息框。但是我的单元测试也测试了错误处理代码,我不希望在运行单元测试时弹出消息框。

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

    下面是它的单元测试:

        [TestMethod]
        public void IsInUnitTest()
        {
            Assert.IsTrue(UnitTestDetector.IsInUnitTest, 
                "Should detect that we are running inside a unit test."); // lol
        }
    
        4
  •  17
  •   Jürgen Steinblock    10 年前

    我用一种和塔尔塞斯相似的方法

    这是基本的代码,可以很容易地修改为包括缓存。 另一个好主意是在 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;
            }
        }
    }
    
        5
  •  11
  •   cdiggins    9 年前

    简化Ryan的解决方案,只需向任何类添加以下静态属性:

        public static readonly bool IsRunningFromNUnit = 
            AppDomain.CurrentDomain.GetAssemblies().Any(
                a => a.FullName.ToLowerInvariant().StartsWith("nunit.framework"));
    
        6
  •  10
  •   Tomas Kubes    9 年前

    可能有用,检查当前进程名:

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

    参考文献:
    马修·沃森 http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/11e68468-c95e-4c43-b02b-7045a52b407e/

        7
  •  9
  •   shA.t Rami Jamleh    6 年前

    在测试模式下, Assembly.GetEntryAssembly() 似乎是 null .

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

    注意如果 assembly.getEntryassembly()。 无效的 , Assembly.GetExecutingAssembly() 不是。

    这个 documentation 说: GetEntryAssembly 方法可以返回 无效的 从非托管应用程序加载托管程序集时。

        8
  •  7
  •   Edward Brey    8 年前

    在正在测试的项目中:

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

    在单元测试项目中的某个地方:

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

    优雅,不。但是直截了当和快速。 AssemblyInitializer 用于MS测试。我希望其他测试框架也有等价物。

        9
  •  3
  •   Graeme Wicksted    13 年前

    我用这个 只有 用于跳过在没有附加调试程序的情况下启动期间禁用log4net中所有traceAppender的逻辑。这样,即使在非调试模式下运行,单元测试也可以记录到Resharper结果窗口。

    使用此函数的方法要么在应用程序启动时调用,要么在开始测试夹具时调用。

    它类似于Ryan的帖子,但使用LINQ,删除系统。反射要求,不缓存结果,并且是私有的,以防止(意外)误用。

        private static bool IsNUnitRunning()
        {
            return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.ToLowerInvariant().StartsWith("nunit.framework"));
        }
    
        10
  •  1
  •   tallseth    12 年前

    最近我对这个问题很不高兴。我用一种稍微不同的方法解决了它。首先,我不愿意假设nunit框架永远不会在测试环境之外加载;我特别担心开发人员在他们的机器上运行应用程序。所以我改为走到了呼叫栈。第二,我能够假设测试代码永远不会针对发行版二进制文件运行,所以我确保这个代码不存在于发行版系统中。

    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
            }
    
        11
  •  1
  •   tom nobleman    9 年前

    很有魅力

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

    .

        12
  •  0
  •   Jürgen Steinblock    7 年前

    考虑到您的代码是在Windows窗体应用程序的主(GUI)线程中正常运行的,并且您希望它在您可以检查的测试中运行时表现不同

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

    我用这个来编写我想要的代码 fire and forgot 在一个GUI应用程序中,但是在单元测试中,我可能需要一个断言的计算结果,并且我不想干扰多个线程的运行。

    适用于MSTEST。我的代码不需要检查测试框架本身的优点是,如果我在某个测试中确实需要异步行为,我可以设置自己的同步上下文。

    请注意,这不是一个可靠的方法 Determine if code is running as part of a unit test 正如op所要求的,因为代码可以在线程内运行,但对于某些场景,这可能是一个很好的解决方案(另外:如果我已经从后台线程运行,则可能不需要启动新的线程)。

        13
  •  0
  •   ChessMax    6 年前

    引用nunit框架并不意味着测试实际上正在运行。例如,在Unity中,当您激活播放模式测试时,nunit引用将添加到项目中。当你运行一个游戏时,引用是存在的,所以UnitTestDetector不能正常工作。

    与检查nunit程序集不同,我们可以要求nunit api检查是否正在执行测试。

    using NUnit.Framework;
    
    // ...
    
    if (TestContext.CurrentContext != null)
    {
        // nunit test detected
        // Do some setup to avoid error
    }
    
        14
  •  0
  •   Glaucus    6 年前

    在单元测试仪下运行时,电流为空。至少对于我的WPF应用程序,使用MS单元测试仪。如果需要的话,这是一个简单的测试。另外,在使用应用程序时要记住的一些东西。在代码中是最新的。

        15
  •  0
  •   TomShantisoft    6 年前

    我在代码中使用了VB中的以下内容来检查单元测试中是否存在AE。尤其是我不想让考试开场白

        If Not Application.ProductName.ToLower().Contains("test") then
            ' Do something 
        End If
    
        16
  •  -3
  •   Danmar Herholdt    11 年前

    在测试类时,还有一个非常简单的解决方案…

    只需给您要测试的类这样的属性:

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

    现在,单元测试可以将“thisisUnitTest”布尔值设置为true,因此在要跳过的代码中,添加:

       if (thisIsUnitTest)
       {
           return;
       } 
    

    它比检查组件更容易、更快。让我想起RubyonRails,在这里您可以查看自己是否在测试环境中。