wake-up-neo.com

Verspottung statischer Methoden

Ich habe vor kurzem angefangen, Moq für den Gerätetest zu verwenden. Ich benutze Moq, um Klassen zu verspotten, die ich nicht testen muss.

Wie gehen Sie normalerweise mit statischen Methoden um?

public void foo(string filePath)
{
    File f = StaticClass.GetFile(filePath);
}

Wie konnte diese statische Methode, StaticClass.GetFile(), verspottet werden?

P.S. Ich würde mich freuen, wenn Sie Lesematerial zu Moq und Unit Testing empfehlen.

63
Kevin Meredith

Scheinrahmen wie Moq oder Rhinomocks können nur Scheininstanzen von Objekten erstellen. Dies bedeutet, dass statische Methoden nicht simuliert werden dürfen.

Sie können auch Google durchsuchen für weitere Informationen.

Außerdem gibt es einige Fragen, die zuvor zu StackOverflow hier , hier und hier gestellt wurden.

35
Pure.Krome

@ Pure.Krome: gute Antwort, aber ich werde ein paar Details hinzufügen 

@ Kevin: Sie müssen eine Lösung auswählen, abhängig von den Änderungen, die Sie am Code vornehmen können.
Wenn Sie es ändern können, wird der Code durch eine Abhängigkeitsinjektion besser überprüfbar. Wenn dies nicht möglich ist, benötigen Sie eine gute Isolation.
Mit kostenlosem Mocking-Framework (Moq, RhinoMocks, NMock ...) können Sie nur Delegaten, Schnittstellen und virtuelle Methoden nachahmen. Für statische, versiegelte und nicht virtuelle Methoden stehen Ihnen drei Lösungen zur Verfügung: 

  • TypeMock Isolator (kann alles verspotten, ist aber teuer) 
  • JustMock von Telerik (Neuling, weniger teuer, aber immer noch nichtfrei) 
  • Moles von Microsoft (die einzige freie Lösung zur Isolation)

Ich empfehle Moles, weil es kostenlos, effizient ist und Lambda-Ausdrücke wie Moq verwendet. Nur ein wichtiges Detail: Maulwürfe bieten Stummel, keine Spötter. So können Sie Moq weiterhin für Interface und Delegierte verwenden;) 

Mock: eine Klasse, die eine Schnittstelle implementiert und die Möglichkeit gibt, die Werte dynamisch festzulegen, die zurückgegeben werden sollen, bzw. Ausnahmen, die von bestimmten Methoden geworfen werden, und die Möglichkeit bietet, zu überprüfen, ob bestimmte Methoden aufgerufen wurden oder nicht.
Stub: Wie eine Scheinklasse, mit der Ausnahme, dass sie nicht die Möglichkeit bietet, zu überprüfen, ob Methoden aufgerufen/nicht aufgerufen wurden.

41
Jeco

In .NET besteht die Möglichkeit, MOQ und jede andere Mocking-Bibliothek auszuschließen. Sie müssen mit der rechten Maustaste auf Solution Explorer in Assembly klicken und die statische Methode, die Sie verspotten möchten, auswählen. Wählen Sie Fakes Assembly. Als Nächstes können Sie die statischen Methoden von Assembly frei nachahmen.

Angenommen, Sie möchten die System.DateTime.Now-statische Methode verspotten. Tun Sie dies zum Beispiel so:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
    Assert.AreEqual(DateTime.Now.Year, 1837);
}

Sie haben ähnliche Eigenschaften für jede statische Eigenschaft und Methode.

14
pt12lol

Sie können dies mit der Pose - Bibliothek von nuget erreichen. Es erlaubt Ihnen, unter anderem statische Methoden zu verspotten. Schreiben Sie in Ihrer Testmethode Folgendes:

Shim shim = Shim.Replace(() => StaticClass.GetFile(Is.A<string>()))
    .With((string name) => /*Here return your mocked value for test*/);
var sut = new Service();
PoseContext.Isolate(() =>
    result = sut.foo("filename") /*Here the foo will take your mocked implementation of GetFile*/, shim);

Weitere Informationen finden Sie hier https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-ohne-interessante-interfaces-978d372478d8

4
mr100

Ich habe mit einem Konzept zum Umgestalten der statischen Methoden zum Aufrufen eines Delegaten herumgespielt, den Sie zu Testzwecken extern festlegen können.

Dies würde kein Testframework verwenden und wäre eine vollständig maßgeschneiderte Lösung. Der Refaktor beeinflusst jedoch nicht die Signatur Ihres Anrufers und ist daher relativ sicher.

Damit dies funktioniert, müssen Sie Zugriff auf die statische Methode haben, sodass es für externe Bibliotheken wie System.DateTime nicht funktioniert.

Hier ist ein Beispiel, mit dem ich gespielt habe, wo ich ein paar statische Methoden erstellt habe, eine mit einem Rückgabetyp, der zwei Parameter enthält, und eine generische, die keinen Rückgabetyp hat.

Die statische Hauptklasse:

public static class LegacyStaticClass
{
    // A static constructor sets up all the delegates so production keeps working as usual
    static LegacyStaticClass()
    {
        ResetDelegates();
    }

    public static void ResetDelegates()
    {
        // All the logic that used to be in the body of the static method goes into the delegates instead.
        ThrowMeDelegate = input => throw input;
        SumDelegate = (a, b) => a + b;
    }

    public static Action<Exception> ThrowMeDelegate;
    public static Func<int, int, int> SumDelegate;

    public static void ThrowMe<TException>() where TException : Exception, new()
        => ThrowMeDelegate(new TException());

    public static int Sum(int a, int b)
        => SumDelegate(a, b);
}

Die Unit-Tests (xUnit und Shouldly)

public class Class1Tests : IDisposable
{
    [Fact]
    public void ThrowMe_NoMocking_Throws()
    {
        Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
    }

    [Fact]
    public void ThrowMe_EmptyMocking_DoesNotThrow()
    {
        LegacyStaticClass.ThrowMeDelegate = input => { };

        LegacyStaticClass.ThrowMe<Exception>();

        true.ShouldBeTrue();
    }

    [Fact]
    public void Sum_NoMocking_AddsValues()
    {
        LegacyStaticClass.Sum(5, 6).ShouldBe(11);
    }

    [Fact]
    public void Sum_MockingReturnValue_ReturnsMockedValue()
    {
        LegacyStaticClass.SumDelegate = (a, b) => 6;
        LegacyStaticClass.Sum(5, 6).ShouldBe(6);
    }

    public void Dispose()
    {
        LegacyStaticClass.ResetDelegates();
    }
}
1
Joe_DM

Ich mochte Pose, konnte aber nicht damit aufhören, InvalidProgramException zu werfen, was ein bekanntes issue zu sein scheint. Jetzt benutze ich Smocks so:

Smock.Run(context =>
{
    context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));

    // Outputs "2000"
    Console.WriteLine(DateTime.Now.Year);
});
1
sirdank

Ich weiß, dass dies etwas spät ist, aber diese Lösung des Kreisverkehrs erlaubte mir, eine statische Methode mit Moq zu simulieren.

Dafür habe ich eine Klasse erstellt (nennen wir sie Placeholder), deren eine Methode die statische Methode StaticClass.GetFile genannt hat.

public class Placeholder{  

    //some empty constructor

    public File GetFile(){

        File f = StaticClass.GetFile(filePath);
        return f;
    }
}

Anstatt StaticClass.GetFile in foo aufzurufen, erstellte ich eine Instanz von Placeholder und rief die GetFile-Funktion auf. 

public void foo(string filePath)
{
    Placeholder p = new Placeholder();
    File f = p.GetFile(filePath);
}

In Einzeltests konnte ich nun, anstatt StaticClass.GetFile zu verspotten, die nicht statische GetFile-Methode der Placeholder-Klasse nachahmen. 

0
Justin Borromeo