142

I have a unit test (nUnit). Many layers down the call stack a method will fail if it is running via a unit test.

Ideally you would use something like mocking to setup the object that this method is depending on but this is 3rd party code and I can't do that without a lot of work.

I don't want setup nUnit specific methods - there are too many levels here and its a poor way of doing unit test.

Instead what I would like to do is to add something like this deep down in the call stack

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

So any ideas about how to write IsRunningInUnitTest?

P.S. I am fully aware that this is not great design, but I think its better than the alternatives.

Ryan
  • 23,871
  • 24
  • 86
  • 132
  • 5
    You should not be directly or indirectly testing third-party code in a unit test. You should isolate your method under test from the third-party implementation. – Craig Stuntz Jul 02 '10 at 16:54
  • 30
    Yes - I realise that - in an idea world, but sometimes we've got to be a bit pragmatic about things no? – Ryan Jul 02 '10 at 16:59
  • 16
    Coming back to Craig's comment - not sure thats true. If my method relies upon the 3rd party library behaving in a certain way then shouldn't this be part of the test? If the 3rd party app changes I want my test to fail. If you're using mocks your testing against how you think the 3rd party app works, not how it actually does. – Ryan Jul 02 '10 at 18:01
  • 2
    Ryan, you can test assumptions about the third party behavior, but *that's a separate test.* You need to test your own code in isolation. – Craig Stuntz Jul 02 '10 at 18:42
  • 5
    I do get what your saying but for anything but a trivial example you would be talking about an large (huge) amount of work and there is nothing to ensure that the assumptions your checking in your test are the same as your assumptions in your actual methods. Hmm - debate for a blog post I think, I'll shoot you an email when I've got my thoughts together. – Ryan Jul 02 '10 at 20:05

21 Answers21

104

I've done this before - I had to hold my nose while I did it, but I did it. Pragmatism beats dogmatism every time. Of course, if there is a nice way you can refactor to avoid it, that would be great.

Basically I had a "UnitTestDetector" class which checked whether the NUnit framework assembly was loaded in the current AppDomain. It only needed to do this once, then cache the result. Ugly, but simple and effective.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    any sample about UnitTestDetector? and similar for MSTest ? – Kiquenet Jun 05 '13 at 08:56
  • 4
    @Kiquenet: I think I'd just use `AppDomain.GetAssemblies` and check for the relevant assembly - for MSTest you'd need to look at which assemblies are loaded. Look at Ryan's answer for an example. – Jon Skeet Jun 05 '13 at 09:14
  • This is not a good approach for me. I'm calling a UnitTest method from a Console App and it thinks it's a UnitTest App. – Bizhan Aug 01 '19 at 14:31
  • 1
    @Bizhan: I'd suggest you're in a rather specialized situation then, and you shouldn't expect more general answers to work. You might want to ask a new question with all of your specific requirements. (What's the difference between "your code calling from a console application" and "a test runner" for example? How would you want to distinguish between your console application and any other console-based test runner?) – Jon Skeet Aug 01 '19 at 14:41
  • @Kiquenet "Microsoft.VisualStudio.TestPlatform.MSTestAdapter" – Joel Wiklund Dec 04 '20 at 15:58
  • I think Jon is a brilliant developer but this answer is far from his best work. There are some really good answers below accompanied by full-fledged code so let's not forget to scroll folks! – Jacksonkr May 15 '23 at 17:13
91

Taking Jon's idea this is what I came up with -

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

Pipe down at the back we're all big enough boys to recognise when we're doing something we probably shouldn't ;)

Nij
  • 2,028
  • 2
  • 22
  • 27
Ryan
  • 23,871
  • 24
  • 86
  • 132
  • 2
    +1 Good answer. This can be simplified quite a bit though, see below: http://stackoverflow.com/a/30356080/184528 – cdiggins May 20 '15 at 17:05
  • 1
    The particular project that I wrote this for was (and still is!) .NET 2.0 so no linq. – Ryan May 21 '15 at 10:23
  • This use to work for me but it seems like the assembly name have changed since. I switched to [Kiquenet's solution](https://stackoverflow.com/a/16935982/315493) – The_Black_Smurf Jun 27 '17 at 20:06
  • I had to turn off logging for travis ci builds, it froze everything – jjxtra Apr 17 '19 at 17:47
  • Works for me, I have to hack around .NET core 3 bugs with razor that only happen in unit tests. – jjxtra Oct 19 '19 at 01:51
  • I'm there with legacy code, started re-implementing the design correctly, realised that it was going to take me a month of Sundays. Just can't do it by the deadline. Great idea, saved my bacon, thanks :) – Nick Aug 12 '22 at 15:24
70

Adapted from Ryan's answer. This one is for the MS unit test framework.

The reason I need this is because I show a MessageBox on errors. But my unit tests also test the error handling code, and I don't want a MessageBox to pop up when running unit tests.

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

And here's a unit test for it:

    [TestMethod]
    public void IsInUnitTest()
    {
        Assert.IsTrue(UnitTestDetector.IsInUnitTest, 
            "Should detect that we are running inside a unit test."); // lol
    }
dan-gph
  • 16,301
  • 12
  • 61
  • 79
  • 8
    I have a better way that solves your MessageBox problem and voids this hack and delivers more unit test cases. I use a class that implements an interface I call ICommonDialogs. The implementation class displays all pop up dialogs (MessageBox, File dialogs, Color picker, database connection dialog etc). Classes that need to display message boxes accept ICommonDiaglogs as a constructor parameter that we can then mock in the unit test. Bonus: You can assert on expected MessageBox calls. – Tony O'Hagan May 01 '12 at 04:31
  • 1
    @Tony, good idea. That's clearly the best way to do it. I don't know what I was thinking at the time. I think dependency injection was still new to me at that time. – dan-gph Jan 16 '13 at 08:09
  • 3
    Seriously, people, learn about dependency injection, and secondarily, mock objects. Dependency injection will revolutionize your programming. – dan-gph Jun 25 '14 at 00:46
  • 2
    I will implement UnitTestDetector.IsInUnitTest as "return true" and your unit test will pass. ;) One of those funny things that seems impossible to unit test. – Samer Adra Feb 09 '18 at 15:46
  • 2
    Microsoft.VisualStudio.QualityTools.UnitTestFramework didn't work for me anymore. Changed it to Microsoft.VisualStudio.TestPlatform.TestFramework - that works again. – Alexander Sep 10 '18 at 14:49
  • Nice solution, I changed the string to "Microsoft.VisualStudio.TestPlatform.TestFramework" for MSTest V2. To folks proposing dependency injection and interfaces etc... Sometimes you're faced with having to quickly write a non-intrusive test for some legacy code in order to fix a bug on a deadline. In this scenario refactoring and re-writing the code is not always feasible and would cause a huge amount of trauma to the code. Yes, better to design it correctly in the first place, but not always feasible in real world conditions. – Richard Moore May 31 '19 at 14:22
  • @RichardMoore, I think you could do "manual" dependency injection without any trouble. For the dialog box example, you could just define some interface—let's call it `IShowMessage`—that has a `ShowMessage` method. Pass in an appropriate object of that type to your constructor in your code. In your unit functions, you just use a test `IShowMessage` class which has a do-nothing `ShowMessage` method. The fancy name for that is the Strategy Pattern. You don't need to use any special libraries or big changes to your code to do that. – dan-gph Jun 03 '19 at 05:55
  • @dan-gph "manual" dependency injection can still be a nightmare with legacy code. If it's doable then great, but sometimes you just don't have that option in the real world. – Richard Moore Jun 03 '19 at 16:16
  • For .NET Core: `string testAssemblyName = "Microsoft.TestPlatform";` – Gabriel Anderson Jul 24 '19 at 11:41
46

Simplifying Ryan's solution, you can just add the following static property to any class:

    public static readonly bool IsRunningFromNUnit = 
        AppDomain.CurrentDomain.GetAssemblies().Any(
            a => a.FullName.ToLowerInvariant().StartsWith("nunit.framework"));
cdiggins
  • 17,602
  • 7
  • 105
  • 102
  • 2
    Pretty much the same as dan-gph's answer (though that was looking for the VS toolset, not nunit). – Ryan May 21 '15 at 10:22
  • 1
    Can be used with xunit as well by replacing the StartsWith part with "xunit.runner" – Rok Jan 29 '22 at 18:53
26

I use a similar approach as tallseth

This is the basic code which could be easily modified to include caching. Another good idea would be to add a setter to IsRunningInUnitTest and call UnitTestDetector.IsRunningInUnitTest = false to your projects main entry point to avoid the code execution.

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;
        }
    }
}
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
  • 1
    I like this approach more than the higher-voted answers. I don't think it's safe to assume that unit test assemblies will only ever be loaded during a unit test and the process name can also vary from developer to developer (eg. some use the R# test runner). – EM0 Oct 02 '15 at 13:11
  • This approach would work but it will be looking for those attributes each time *IsRunningInUnitTest* getter is called. There could be some cases in which it can affect performance. Checking the *AssemblyName* is cheaper because it's done only once. The idea with the public setter is good but in this case, *UnitTestDetector* class should be placed in a shared assembly. – Serg Feb 11 '18 at 14:50
  • FWIW: NUnit *can* run tests without such attributes (legacy test detection). Of course, it's probably easiest to just update the test cases in that case.. – user2864740 Feb 10 '21 at 00:13
  • This will FAIL in code that runs on a different thread context, and possibly even in static type initialization (depending on usages). – user2864740 Feb 10 '21 at 00:16
  • As far as performance: a cache variable will take care of that as it's generally safe to assume that if running in a test once, it's running it a test everywhere.. which in turn will hide issues from #2. It could also be used in conjunction with an assembly check. – user2864740 Feb 10 '21 at 00:18
  • When adapting for current tests, I ran into two issues: 1) Tests have a TestCaseSourceAttribute without a TestFixtureAttribute (using a pattern instead: NUnit.Framework..Attribute) and 2) TestCaseFixture can be _inherited_, so `GetCustomAttributes(true)` is required to detect that case. – user2864740 Feb 10 '21 at 02:40
16

Maybe useful, checking current 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
    }
}

And this function should be also check by unittest:

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

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

Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
Kiquenet
  • 14,494
  • 35
  • 148
  • 243
  • 1
    `|| processName.StartsWith("testhost") // testhost.x86` for VS 2019 – Kiquenet Mar 13 '20 at 17:38
  • This fails under ReSharper tests (as does the "testhost" too). These checks are all so brittle. – user2864740 Feb 10 '21 at 00:12
  • @user2864740 have you solution for ReSharper tests ? ReSharper is third party not free, not included in Visual Studio. – Kiquenet Feb 18 '21 at 09:01
  • I'm merely pointing out that it doesn't run on all test runners. Sadly, while the VS Test runner and dotnet test runner set the executing assembly name to "testhost", such is not universal (I'm not sure of XUnit or MBUnit.. if anyone still uses those). The solution that we've adopted makes multiple checks: executing assembly name, process name, then for a NUnit reference (and fixture attribute in the call-stack, similar to shown in other answers here). It is still brittle, as all of those are artifacts to specific test runners/frameworks that someone might need to update later. :-/ – user2864740 Feb 19 '21 at 01:07
14

Somewhere in the project being tested:

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

Somewhere in your unit test project:

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

Elegant, no. But straightforward and fast. AssemblyInitializer is for MS Test. I would expect other test frameworks to have equivalents.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
  • 1
    If the code you are testing creates additional AppDomains, `IsRunningInUnitTest` doesn't get set to true in those AppDomains. – Edward Brey Jul 16 '15 at 01:41
  • But it could be easily solved by adding a shared assembly or declaring *IsRunningInUnitTest* in each domain. – Serg Feb 11 '18 at 15:04
  • For NUnit 3+, the appropriate place would probably be OneTimeSetUp. – bdrajer Jan 21 '21 at 11:49
13

In test mode, Assembly.GetEntryAssembly() seems to be null.

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

Note that if Assembly.GetEntryAssembly() is null, Assembly.GetExecutingAssembly() isn't.

The documentation says: The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application.

shA.t
  • 16,580
  • 5
  • 54
  • 111
Eric Bole-Feysot
  • 13,949
  • 7
  • 47
  • 53
  • 2
    This behavior has changed since the introduction of dotnetcore. Even in 2012 this reports false positives in contexts like IIS. – user2864740 Feb 10 '21 at 00:10
5

Just use this:

AppDomain.CurrentDomain.IsDefaultAppDomain()

In test mode, it will return false.

shinexyt
  • 59
  • 1
  • 3
4

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.

Bizhan
  • 16,157
  • 9
  • 63
  • 101
ChessMax
  • 2,125
  • 1
  • 16
  • 16
  • 1
    This requires an explicit reference to NUnit in "possible non-test code". Perhaps use an NUnit assembly check *and reflection* to test for the context if the assembly is loaded. – user2864740 Feb 10 '21 at 00:09
4

I use this only for skipping logic that disables all TraceAppenders in log4net during startup when no debugger is attached. This allows unit tests to log to the Resharper results window even when running in non-debug mode.

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"));
    }
Graeme Wicksted
  • 1,770
  • 1
  • 18
  • 21
4

I have a solution that's closer to what the original poster wanted. The issue is how to set the test flag to indicate the code is executing as part of a test. This can be implemented with 2 lines of code.

I have added an internal variable called RunningNunitTest at the top of the class. Be sure to make this an internal variable and not public. We don't want to export this variable when we build the project. Also this is how we're going to allow NUnit to set it to true.

NUnit does not have access to private variables or methods in our code. This is an easy fix. In between the using statements and the namespace add a [assembly: InternalsVisibleTo("NUnitTest")] decoration. This allows NUint access to any internal variable or method. My NUnit test project is named "NUintTest." Replace this name with the name of your NUint test Project.

That's it! Set RunningNunitTest to true in your NUnit tests.

using NetworkDeviceScanner;

[assembly: InternalsVisibleTo("NUnitTest")] // Add this decoration to your class

namespace NetworkDeviceScannerLibrary
{
    public class DetectDevice
    {
        internal bool RunningNunitTest = false; // Add this variable to your class

        public ulong TotalAddressesFound;
        public ulong ScanCount;

NUnit Code

var startIp = IPAddress.Parse("191.168.1.1");
var endIp = IPAddress.Parse("192.168.1.128");
var detectDevice = new DetectDevice
{
    RunningNunitTest = true
};
Assert.Throws<ArgumentOutOfRangeException>(() => detectDevice.DetectIpRange(startIp, endIp, null));
FrankJames
  • 100
  • 1
3

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

.

tom nobleman
  • 366
  • 3
  • 8
3

Unit tests will skip application entry point. At least for wpf, winforms and console application main() is not being called.

If main method is called than we are in run-time, otherwise we are in unit test mode:

public static bool IsUnitTest { get; private set; } = true;

[STAThread]
public static void main()
{
    IsUnitTest = false;
    ...
}
Sinatr
  • 20,892
  • 15
  • 90
  • 319
2

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
        }
tallseth
  • 3,635
  • 1
  • 23
  • 24
  • I find the idea of testing the debug version while shipping the release version to be a bad idea in general. – Patrick M Jun 08 '15 at 06:37
2

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.

Danmar Herholdt
  • 215
  • 2
  • 5
2

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.

Glaucus
  • 848
  • 8
  • 14
2
            if (string.IsNullOrEmpty(System.Web.Hosting.HostingEnvironment.MapPath("~")))
            {
                // Running not as a web app (unit tests)
            }

            // Running as a web app
  • How does this work? It feels like you are relying on some undocumented behaviour. – Buh Buh Sep 24 '21 at 10:09
  • https://learn.microsoft.com/en-us/dotnet/api/system.web.hosting.hostingenvironment.mappath?view=netframework-4.8 It says: Returns The physical path on the *server* specified by virtualPath. In case of console app, unit test or even desktop app there is no server at all – Abd El Rahman Mohammed Sep 24 '21 at 10:49
1

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).

Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
1

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
1

How about using reflection and something like this:

var underTest = Assembly.GetCallingAssembly() != typeof(MainForm).Assembly;

The calling assembly will be where your test cases are and just substitute for MainForm some type that's in your code being tested.

Jon
  • 121
  • 1
  • 2
  • But you cannot expect the caller to be test assembly always. The calling assembly's caller could be the unit test assembly. Hard to say – Soundararajan Dec 24 '20 at 17:27