webentwicklung-frage-antwort-db.com.de

Wie statische Methoden in c # mit MOQ-Framework verspotten?

Ich habe kürzlich Unit-Tests durchgeführt und mit MOQ-Framework und MS-Test erfolgreich verschiedene Szenarien verspottet. Ich weiß, dass wir keine privaten Methoden testen können, aber ich möchte wissen, ob wir statische Methoden mit MOQ verspotten können.

48
Himanshu

Moq (und andere DynamicProxy - basierte Verspottungs-Frameworks) können nichts verspotten, was keine virtuelle oder abstrakte Methode ist.

Versiegelte/statische Klassen/Methoden können nur mit Profiler API-basierten Tools wie Typemock (kommerziell) oder Microsoft Moles (kostenlos, bekannt als Fälschungen in Visual Studio 2012 Ultimate gefälscht werden. 2013/2015).

Alternativ können Sie Ihren Entwurf überarbeiten, um Aufrufe statischer Methoden zu abstrahieren, und diese Abstraktion Ihrer Klasse über die Abhängigkeitsinjektion bereitstellen. Dann hätten Sie nicht nur ein besseres Design, sondern können es auch mit kostenlosen Tools wie Moq testen.

Ein allgemeines Muster zur Ermöglichung der Testbarkeit kann angewendet werden, ohne dass Werkzeuge verwendet werden müssen. Betrachten Sie die folgende Methode:

public class MyClass
{
    public string[] GetMyData(string fileName)
    {
        string[] data = FileUtil.ReadDataFromFile(fileName);
        return data;
    }
}

Anstatt zu versuchen, FileUtil.ReadDataFromFile Zu verspotten, können Sie es in eine protected virtual - Methode einschließen:

public class MyClass
{
    public string[] GetMyData(string fileName)
    {
        string[] data = GetDataFromFile(fileName);
        return data;
    }

    protected virtual string[] GetDataFromFile(string fileName)
    {
        return FileUtil.ReadDataFromFile(fileName);
    }
}

Leiten Sie dann in Ihrem Komponententest von MyClass ab und nennen Sie es TestableMyClass. Anschließend können Sie die Methode GetDataFromFile überschreiben, um Ihre eigenen Testdaten zurückzugeben.

Hoffentlich hilft das.

89
Igal Tabachnik

Eine weitere Option, um die statische Methode in eine statische Funktion oder Aktion umzuwandeln. Zum Beispiel.

Originalcode:

    class Math
    {
        public static int Add(int x, int y)
        {
            return x + y;
        }

Sie möchten die Add-Methode "verspotten", können dies jedoch nicht. Ändern Sie den obigen Code in:

        public static Func<int, int, int> Add = (x, y) =>
        {
            return x + y;
        };

Vorhandener Client-Code muss nicht geändert (möglicherweise neu kompiliert) werden, die Quelle bleibt jedoch gleich.

Um das Verhalten der Methode nach dem Unit-Test zu ändern, weisen Sie ihr einfach eine Inline-Funktion zu:

    [TestMethod]
    public static void MyTest()
    {
        Math.Add = (x, y) =>
        {
            return 11;
        };

Fügen Sie die gewünschte Logik in die Methode ein oder geben Sie einfach einen fest codierten Wert zurück, je nachdem, was Sie tun möchten.

Dies muss nicht immer möglich sein, aber in der Praxis hat sich diese Technik bewährt.

[Bearbeiten] Ich schlage vor, dass Sie Ihrer Unit-Test-Klasse den folgenden Bereinigungscode hinzufügen:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Math).TypeInitializer.Invoke(null, null);
    }

Fügen Sie für jede statische Klasse eine separate Zeile hinzu. Nachdem der Unit-Test ausgeführt wurde, werden alle statischen Felder auf ihren ursprünglichen Wert zurückgesetzt. Auf diese Weise beginnen andere Komponententests im selben Projekt mit den richtigen Standardeinstellungen im Gegensatz zu Ihrer verspotteten Version.

42
zumalifeguard

Wie in den anderen Antworten erwähnt, kann MOQ statische Methoden nicht verspotten, und in der Regel sollte man statische Methoden nach Möglichkeit vermeiden.

Manchmal ist es nicht möglich. Man arbeitet mit Legacy- oder Drittanbieter-Code oder sogar mit den BCL-Methoden, die statisch sind.

Eine mögliche Lösung besteht darin, das Static in einen Proxy mit einer Schnittstelle zu packen, die nachgeahmt werden kann

    public interface IFileProxy {
        void Delete(string path);
    }

    public class FileProxy : IFileProxy {
        public void Delete(string path) {
            System.IO.File.Delete(path);
        }
    }

    public class MyClass {

        private IFileProxy _fileProxy;

        public MyClass(IFileProxy fileProxy) {
            _fileProxy = fileProxy;
        }

        public void DoSomethingAndDeleteFile(string path) {
            // Do Something with file
            // ...
            // Delete
            System.IO.File.Delete(path);
        }

        public void DoSomethingAndDeleteFileUsingProxy(string path) {
            // Do Something with file
            // ...
            // Delete
            _fileProxy.Delete(path);

        }
    }

Der Nachteil ist, dass der ctor bei vielen Proxys sehr unübersichtlich werden kann (obwohl argumentiert werden könnte, dass die Klasse bei vielen Proxys möglicherweise versucht, zu viel zu tun, und überarbeitet werden könnte).

Eine andere Möglichkeit besteht darin, einen "statischen Proxy" mit verschiedenen Implementierungen der Schnittstelle dahinter zu haben

   public static class FileServices {

        static FileServices() {
            Reset();
        }

        internal static IFileProxy FileProxy { private get; set; }

        public static void Reset(){
           FileProxy = new FileProxy();
        }

        public static void Delete(string path) {
            FileProxy.Delete(path);
        }

    }

Unsere Methode wird jetzt

    public void DoSomethingAndDeleteFileUsingStaticProxy(string path) {
            // Do Something with file
            // ...
            // Delete
            FileServices.Delete(path);

    }

Zum Testen können wir die FileProxy-Eigenschaft auf unseren Mock setzen. Die Verwendung dieses Stils reduziert die Anzahl der zu injizierenden Schnittstellen, macht jedoch Abhängigkeiten weniger offensichtlich (obwohl dies nicht mehr als die ursprünglichen statischen Aufrufe ist, nehme ich an).

7
AlanT

Moq kann ein statisches Mitglied einer Klasse nicht verspotten.

Beim Entwerfen von Code für die Testbarkeit ist es wichtig, statische Member (und Singletons) zu vermeiden. Ein Entwurfsmuster, mit dessen Hilfe Sie den Code auf Testbarkeit umgestalten können, ist die Abhängigkeitsinjektion.

Das bedeutet, dies zu ändern:

public class Foo
{
    public Foo()
    {
        Bar = new Bar();
    }
}

zu

public Foo(IBar bar)
{
    Bar = bar;
}

Auf diese Weise können Sie einen Schein aus Ihren Unit-Tests verwenden. In der Produktion verwenden Sie ein Abhängigkeitsinjektionswerkzeug wie Ninject oder nity , das alles miteinander verbinden kann.

Ich habe vor einiger Zeit einen Blog darüber geschrieben. Es wird erklärt, welche Muster für einen besser testbaren Code verwendet werden können. Vielleicht kann es dir helfen: nit Testing, Hölle oder Himmel?

Eine andere Lösung könnte sein, das Microsoft Fakes Framework zu verwenden. Dies ist kein Ersatz für das Schreiben von gut entworfenem, testbarem Code, aber es kann Ihnen helfen. Mit dem Fakes-Framework können Sie statische Member verspotten und zur Laufzeit durch Ihr eigenes benutzerdefiniertes Verhalten ersetzen.

6
Wouter de Kort