webentwicklung-frage-antwort-db.com.de

Stellen Sie fest, ob der Code im Rahmen eines Komponententests ausgeführt wird

Ich habe einen Unit-Test (nUnit). In vielen Schichten des Aufrufstapels schlägt eine Methode fehl, wenn sie über einen Komponententest ausgeführt wird.

Idealerweise würden Sie das Objekt, von dem diese Methode abhängt, mit einer Art Spott einrichten, aber dies ist Code von Drittanbietern, und ich kann das nicht ohne viel Arbeit tun.

Ich möchte keine Setup-nUnit-spezifischen Methoden verwenden. Es gibt zu viele Ebenen und es ist eine schlechte Methode, einen Unit-Test durchzuführen.

Stattdessen möchte ich so etwas tief im Call-Stack einfügen

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

Also irgendwelche Ideen, wie man IsRunningInUnitTest schreibt?

P.S. Ich bin mir völlig bewusst, dass dies kein großartiges Design ist, aber ich denke es ist besser als die Alternativen.

89
Ryan

Ich habe das schon mal gemacht - ich musste meine Nase dabei halten, aber ich tat es. Pragmatismus schlägt jedes Mal den Dogmatismus. Wenn es gibt Ein schöner Weg, den Sie umgestalten können, um es zu vermeiden, wäre das natürlich großartig.

Im Grunde hatte ich eine "UnitTestDetector" -Klasse, die überprüft hat, ob die NUnit-Framework-Assembly in die aktuelle AppDomain geladen wurde. Das musste nur einmal gemacht werden, dann wurde das Ergebnis zwischengespeichert. Hässlich, aber einfach und effektiv.

71
Jon Skeet

Auf Jons Idee bin ich gekommen -

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 nach hinten, wir sind alle groß genug, um zu erkennen, wenn wir etwas tun, was wir wahrscheinlich nicht tun sollten;)

66
Ryan

Angepasst von Ryans Antwort. Dieses ist für das MS-Unit-Test-Framework.

Der Grund, warum ich das brauche, ist, dass ich eine MessageBox auf Fehler zeige. Meine Unit-Tests testen jedoch auch den Fehlerbehandlungscode, und ich möchte nicht, dass eine MessageBox beim Ausführen von Unit-Tests angezeigt wird.

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

Und hier ist ein Unit-Test dafür:

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

Ich verwende einen ähnlichen Ansatz als Tallseth

Dies ist der grundlegende Code, der leicht geändert werden kann, um das Zwischenspeichern einzuschließen. Eine andere gute Idee wäre, einen Setter zu IsRunningInUnitTest hinzuzufügen und UnitTestDetector.IsRunningInUnitTest = false am Haupteinstiegspunkt Ihres Projekts aufzurufen, um die Ausführung des Codes zu vermeiden.

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

Um die Lösung von Ryan zu vereinfachen, können Sie jeder Klasse einfach die folgende statische Eigenschaft hinzufügen:

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

Vielleicht nützlich, wenn Sie den aktuellen Prozessnamen überprüfen:

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

Und diese Funktion sollte auch von unittest geprüft werden:

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

Verweise:
Matthew Watson in http://social.msdn.Microsoft.com/Forums/en-US/csharplanguage/thread/11e68468-c95e-4c43-b02b-7045a52b407d/

10
Kiquenet

Im Testmodus scheint Assembly.GetEntryAssembly()null zu sein.

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

Beachten Sie, dass wenn Assembly.GetEntryAssembly()null ist, Assembly.GetExecutingAssembly() nicht.

Die documentation sagt: Die GetEntryAssembly-Methode kann null zurückgeben, wenn eine verwaltete Assembly aus einer nicht verwalteten Anwendung geladen wurde.

9

Irgendwo im getesteten Projekt:

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

Irgendwo in Ihrem Gerätetestprojekt:

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

Elegant, nein. Aber unkompliziert und schnell. AssemblyInitializer ist für MS Test. Ich würde erwarten, dass andere Testframeworks Äquivalente haben.

7
Edward Brey

Ich verwende diese only zum Überspringen der Logik, die alle TraceAppenders in log4net während des Startvorgangs deaktiviert, wenn kein Debugger angeschlossen ist. Auf diese Weise können sich Unit-Tests auch im Nicht-Debug-Modus im Resharper-Ergebnisfenster anmelden.

Die Methode, die diese Funktion verwendet, wird entweder beim Start der Anwendung oder beim Starten eines Testgeräts aufgerufen.

Es ist ähnlich wie Ryans Beitrag, verwendet jedoch LINQ, löscht die Anforderung von System.Reflection, speichert das Ergebnis nicht zwischen und ist privat, um (versehentlichen) Missbrauch zu verhindern.

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

Ich war kürzlich unzufrieden mit diesem Problem. Ich habe es etwas anders gelöst. Erstens wollte ich nicht davon ausgehen, dass das Nunit-Framework niemals außerhalb einer Testumgebung geladen wird. Ich war besonders besorgt über die Entwickler, die die App auf ihren Computern ausführen. Also ging ich stattdessen den Call Stack. Zweitens konnte ich davon ausgehen, dass Testcode niemals mit Binärdateien von Releases ausgeführt wird. Daher habe ich sichergestellt, dass dieser Code in einem Releasesystem nicht vorhanden ist.

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
        }
1
tallseth

klappt wunderbar

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

.

1
tom nobleman

Ich habe Folgendes in VB in meinem Code verwendet, um zu überprüfen, ob wir einen Komponententest durchführen. Ich wollte eigentlich nicht, dass der Test Word öffnet

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

Einen Verweis auf das Nunit-Framework zu haben, bedeutet nicht, dass der Test tatsächlich ausgeführt wird. Wenn Sie beispielsweise in Unity Tests für den Wiedergabemodus aktivieren, werden die Nunit-Referenzen zum Projekt hinzugefügt. Wenn Sie ein Spiel ausführen, sind die Verweise vorhanden, sodass UnitTestDetector nicht ordnungsgemäß funktioniert.

Anstatt auf nunit Assembly zu prüfen, können wir nun nunit api überprüfen, ob der Code gerade getestet wird oder nicht.

using NUnit.Framework;

// ...

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

Wenn Sie bedenken, dass Ihr Code normalerweise im Haupt- (gui) -Thread einer Windows Forms-Anwendung ausgeführt wird, möchten Sie, dass er sich in einem Test, auf den Sie prüfen können, anders verhält 

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

Ich verwende dies für Code, den ich in einer GUI-Anwendung fire and forgot möchte, aber in den Unit-Tests brauche ich möglicherweise das berechnete Ergebnis für eine Assertation, und ich möchte nicht, dass mehrere Threads laufen.

Funktioniert für MSTest. Der Vorteil ist, dass mein Code nicht nach dem Testframework selbst suchen muss, und wenn ich wirklich das asynchrone Verhalten in einem bestimmten Test brauche, kann ich meinen eigenen SynchronizationContext festlegen.

Beachten Sie, dass dies keine zuverlässige Methode für Determine if code is running as part of a unit test ist, wie von OP angefordert, da Code in einem Thread ausgeführt werden könnte. In bestimmten Szenarien kann dies jedoch eine gute Lösung sein (auch: Wenn ich bereits einen Hintergrund-Thread ausführte, ist dies möglicherweise nicht der Fall.) notwendig, um ein neues zu beginnen).

0

Application.Current ist null, wenn unter dem Gerätetester ausgeführt wird. Zumindest für meine WPF-App mit MS Unit Tester. Dies ist ein einfacher Test, wenn nötig. Beachten Sie auch, wenn Sie Application.Current in Ihrem Code verwenden.

0
Glaucus