developer tip

코드가 단위 테스트의 일부로 실행 중인지 확인

optionbox 2020. 8. 26. 07:48
반응형

코드가 단위 테스트의 일부로 실행 중인지 확인


단위 테스트 (nUnit)가 있습니다. 호출 스택 아래의 많은 계층에서 단위 테스트를 통해 실행중인 경우 메서드가 실패합니다.

이상적으로이 방법이 의존하는 객체를 설정하기 위해 조롱과 같은 것을 사용하는 것이 좋지만 이것은 타사 코드이며 많은 작업 없이는 할 수 없습니다.

nUnit 특정 방법을 설정하고 싶지 않습니다. 여기에는 레벨이 너무 많고 단위 테스트를 수행하는 방법이 좋지 않습니다.

대신에 제가하고 싶은 것은 콜 스택에 다음과 같은 것을 추가하는 것입니다.

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

IsRunningInUnitTest를 작성하는 방법에 대한 아이디어가 있습니까?

추신 나는 이것이 훌륭한 디자인이 아니라는 것을 완전히 알고 있지만 대안보다 낫다고 생각 합니다.


전에 해본 적이 있습니다.하는 동안 코를 잡아야했지만 해냈습니다. 실용주의는 매번 독단주의를 능가합니다. 물론 를 방지하기 위해 리팩토링 할 수있는 좋은 방법 있다면 좋을 것입니다.

기본적으로 NUnit 프레임 워크 어셈블리가 현재 AppDomain에로드되었는지 확인하는 "UnitTestDetector"클래스가 있습니다. 이 작업을 한 번만 수행 한 다음 결과를 캐시하면됩니다. 추악하지만 간단하고 효과적입니다.


Jon의 생각을 받아들이면 이것이 제가 생각 해낸 것입니다.

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

뒤에서 우리는 우리가 아마하지 말아야 할 일을 할 때 인식 할 수있을만큼 충분히 큰 소년들입니다.)


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

여기에 대한 단위 테스트가 있습니다.

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

나는 tallseth와 비슷한 접근 방식을 사용합니다.

캐싱을 포함하도록 쉽게 수정할 수있는 기본 코드입니다. 또 다른 좋은 아이디어는 코드 실행을 피하기 위해 프로젝트 기본 진입 점에 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의 솔루션을 단순화하면 다음 정적 속성을 모든 클래스에 추가 할 수 있습니다.

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

참조 : http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/11e68468-c95e-4c43-b02b-7045a52b407e/의
Matthew Watson


테스트 모드에서 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()이다 null, Assembly.GetExecutingAssembly()아니다.

문서는 말한다 다음 GetEntryAssembly방법은 반환 할 수 null어셈블리를 관리하는이 관리되지 않는 응용 프로그램에서로드 된 경우.


테스트중인 프로젝트의 어딘가 :

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

우아하지 않습니다. 그러나 간단하고 빠릅니다. AssemblyInitializerMS 테스트 용입니다. 다른 테스트 프레임 워크도 이에 상응 할 것으로 기대합니다.


디버거가 연결되지 않았을 때 시작하는 동안 log4net의 모든 TraceAppender를 비활성화하는 논리를 건너 뛰는 경우에만 이것을 사용합니다 . 이를 통해 비디 버그 모드에서 실행중인 경우에도 단위 테스트가 Resharper 결과 창에 기록 할 수 있습니다.

The method that uses this function is either called on startup of the application or when beginning a test fixture.

It is similar to Ryan's post but uses LINQ, drops the System.Reflection requirement, does not cache the result, and is private to prevent (accidental) misuse.

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

Having a reference to nunit framework doesn't mean that test is actually running. For example in Unity when you activate play mode tests the nunit references are added to the project. And when you run a game the references are exist, so UnitTestDetector would not work correctly.

Instead of checking for nunit assembly we can ask nunit api to check is code under executing test now or not.

using NUnit.Framework;

// ...

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

Edit:

Beware that the TestContext may be automatically generated if it's required.


I was unhappy to have this problem recently. I solved it in a slightly different way. First, I was unwilling to make the assumption that nunit framework would never be loaded outside a test environment; I was particularly worried about developers running the app on their machines. So I walked the call stack instead. Second, I was able to make the assumption that test code would never be run against release binaries, so I made sure this code did not exist in a release system.

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
        }

works like a charm

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

.


Considering your code is normaly run in the main (gui) thread of an windows forms application and you want it to behave different while running in a test you can check for

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

I am using this for code that I want to fire and forgot in a gui application but in the unit tests I might need the computed result for an assertation and I don't want to mess with multiple threads running.

Works for MSTest. The advantage it that my code does not need to check for the testing framework itself and if I really need the async behaviour in a certain test I can set my own SynchronizationContext.

Be aware that this is not a reliable method to Determine if code is running as part of a unit test as requested by OP since code could be running inside a thread but for certain scenarios this could be a good solution (also: If I am already running from a background thread, it might not be necessary to start a new one).


Application.Current is null when running under the unit tester. At least for my WPF app using MS Unit tester. That's an easy test to make if needed. Also, something to keep in mind when using Application.Current in your code.


I've used the following in VB in my code to check if we ae in a unit test. spifically i didn't want the test to open Word

    If Not Application.ProductName.ToLower().Contains("test") then
        ' Do something 
    End If

There is a really simple solution as well when you are testing a class...

Simply give the class you are testing a property like this:

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

Now your unit test can set the "thisIsUnitTest" boolean to true, so in the code you want to skip, add:

   if (thisIsUnitTest)
   {
       return;
   } 

Its easier and faster than inspecting the assemblies. Reminds me of Ruby On Rails where you'd look to see if you are in the TEST environment.

참고URL : https://stackoverflow.com/questions/3167617/determine-if-code-is-running-as-part-of-a-unit-test

반응형