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

Rya*_*yan 96 c# reflection nunit

我有一个单元测试(nUnit).调用堆栈中的许多层如果通过单元测试运行,则方法将失败.

理想情况下,你会使用像mocking这样的东西来设置这个方法所依赖的对象,但这是第三方代码,如果没有大量工作我就不能这样做.

我不想设置nUnit特定的方法 - 这里有太多的级别,并且它是一个很差的单元测试方法.

相反,我想要做的是在调用堆栈中添加类似这样的东西

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

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

PS我完全清楚这不是很好的设计,但我认为它比替代品更好.

Jon*_*eet 73

我以前做过这个 - 我做的时候不得不捏住鼻子,但是我做到了.实用主义每次都击败教条主义.当然,如果你可以重构,以避免它的好办法,那将是巨大的.

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

  • @Kiquenet:我想我只是使用`AppDomain.GetAssemblies`并检查相关的程序集 - 对于MSTest,您需要查看加载的程序集.看看Ryan的答案就是一个例子. (3认同)

Rya*_*yan 72

采取乔恩的想法这就是我想出的 -

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

在后面管道我们都是足够大的男孩,当我们做某事时我们可能不应该认识到;)

  • +1好答案.这可以简化一点,见下文:http://stackoverflow.com/a/30356080/184528 (2认同)

dan*_*gph 55

改编自Ryan的答案.这个是针对MS单元测试框架的.

我需要这个的原因是因为我在错误上显示MessageBox.但我的单元测试也测试了错误处理代码,我不想在运行单元测试时弹出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; }
}
Run Code Online (Sandbox Code Playgroud)

这是一个单元测试:

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

  • 我有一个更好的方法来解决你的MessageBox问题,并使这个hack无效,并提供更多的单元测试用例.我使用一个实现我称之为ICommonDialogs的接口的类.实现类显示所有弹出对话框(MessageBox,文件对话框,颜色选择器,数据库连接对话框等).需要显示消息框的类接受ICommonDiaglogs作为构造函数参数,然后我们可以在单元测试中进行模拟.额外:您可以在预期的MessageBox调用上断言. (8认同)
  • 说真的,人们,了解依赖注入,其次是模拟对象.依赖注入将彻底改变您的编程. (3认同)
  • 我将实现UnitTestDetector.IsInUnitTest作为"return true",你的单元测试将通过.;)其中一个有趣的事情似乎不可能进行单元测试. (2认同)
  • Microsoft.VisualStudio.QualityTools.UnitTestFramework 不再适合我了。将其更改为 Microsoft.VisualStudio.TestPlatform.TestFramework - 再次运行。 (2认同)

Jür*_*ock 18

我使用类似的方法作为高度

这是可以轻松修改以包含缓存的基本代码.另一个好主意是添加一个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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


cdi*_*ins 18

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

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

  • 与dan-gph的答案几乎相同(尽管那是在寻找VS工具集,而不是nunit). (2认同)

Kiq*_*net 11

也许有用,检查当前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
    }
}
Run Code Online (Sandbox Code Playgroud)

此功能也应通过unittest检查:

[TestClass]
public class TestUnittestRunning
{
    [TestMethod]
    public void UnitTestRunningTest()
    {
        Assert.IsTrue(MyTools.UnitTestMode);
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Eri*_*sot 9

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

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

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

文件说:该GetEntryAssembly方法可以返回null时,管理组件已经从非托管应用程序加载.

  • 自从引入 dotnetcore 以来,这种行为已经发生了变化。即使在 2012 年,该报告也会在 IIS 等环境中报告误报。 (2认同)

Edw*_*rey 8

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

public static class Startup
{
    public static bool IsRunningInUnitTest { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 如果您正在测试的代码创建了额外的 AppDomain,则这些 AppDomain 中的“IsRunningInUnitTest”不会设置为 true。 (2认同)

小智 5

只需使用这个:

AppDomain.CurrentDomain.IsDefaultAppDomain()
Run Code Online (Sandbox Code Playgroud)

在测试模式下,它将返回 false。