wake-up-neo.com

Wie benutze ich Moq, um eine Erweiterungsmethode zu verspotten?

Ich schreibe einen Test, der von den Ergebnissen einer Erweiterungsmethode abhängt, aber ich möchte nicht, dass ein zukünftiger Fehler dieser Erweiterungsmethode diesen Test jemals unterbricht. Das Verspotten dieses Ergebnisses schien die naheliegende Wahl zu sein, aber Moq bietet anscheinend keine Möglichkeit, eine statische Methode zu überschreiben (Voraussetzung für eine Erweiterungsmethode). Es gibt eine ähnliche Idee mit Moq.Protected und Moq.Stub, aber sie scheinen nichts für dieses Szenario anzubieten. Vermisse ich etwas oder soll ich das anders angehen?

Hier ist ein triviales Beispiel, das mit dem üblichen "Ungültige Erwartung für ein nicht überschreibbares Mitglied" fehlschlägt. Dies ist ein schlechtes Beispiel für die Notwendigkeit, eine Erweiterungsmethode zu verspotten, aber es sollte reichen.

public class SomeType {
    int Id { get; set; }
}

var ListMock = new Mock<List<SomeType>>();
ListMock.Expect(l => l.FirstOrDefault(st => st.Id == 5))
        .Returns(new SomeType { Id = 5 });

Was alle TypeMock-Junkies betrifft, die möglicherweise vorschlagen, stattdessen Isolator zu verwenden: Ich schätze den Aufwand, da TypeMock den Job scheinbar blind und betrunken erledigen könnte, aber unser Budget wird nicht in Kürze steigen.

75
patridge

Erweiterungsmethoden sind nur statische Methoden in Verkleidung. Das Verspotten von Frameworks wie Moq oder Rhinomocks kann nur verspottete Instanzen von Objekten erstellen. Daher ist das Verspotten von statischen Methoden nicht möglich.

59
Mendelt

Wenn Sie den Erweiterungsmethodencode ändern können, können Sie ihn folgendermaßen codieren, um Folgendes testen zu können:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

public static class MyExtensions
{
    public static IMyImplementation Implementation = new MyImplementation();

    public static string MyMethod(this object obj)
    {
        return Implementation.MyMethod(obj);
    }
}

public interface IMyImplementation
{
    string MyMethod(object obj);
}

public class MyImplementation : IMyImplementation
{
    public string MyMethod(object obj)
    {
        return "Hello World!";
    }
}

Die Erweiterungsmethoden sind also nur ein Wrapper um die Implementierungsschnittstelle.

(Sie könnten nur die Implementierungsklasse ohne Erweiterungsmethoden verwenden, die eine Art syntaktischen Zucker darstellen.)

Außerdem können Sie die Implementierungsschnittstelle verspotten und als Implementierung für die Erweiterungsklasse festlegen.

public class MyClassUsingExtensions
{
    public string ReturnStringForObject(object obj)
    {
        return obj.MyMethod();
    }
}

[TestClass]
public class MyTests
{
    [TestMethod]
    public void MyTest()
    {
        // Given:
        //-------
        var mockMyImplementation = new Mock<IMyImplementation>();

        MyExtensions.Implementation = mockMyImplementation.Object;

        var myObject = new Object();
        var myClassUsingExtensions = new MyClassUsingExtensions();

        // When:
        //-------
        myClassUsingExtensions.ReturnStringForObject(myObject);

        //Then:
        //-------
        // This would fail because you cannot test for the extension method
        //mockMyImplementation.Verify(m => m.MyMethod());

        // This is success because you test for the mocked implementation interface
        mockMyImplementation.Verify(m => m.MyMethod(myObject));
    }
}
21
informatorius

Ich weiß, dass diese Frage seit ungefähr einem Jahr nicht mehr aktiv war, aber Microsoft hat ein Framework veröffentlicht, das genau dies behandelt und Moles heißt.

Hier sind auch einige Tutorials:

  • DimeCasts.net
  • 15
    Mike Fielden

    Ich habe eine Wrapper-Klasse für die Erweiterungsmethoden erstellt, die ich zum Verspotten benötigte.

    public static class MyExtensions
    {
        public static string MyExtension<T>(this T obj)
        {
            return "Hello World!";
        }
    }
    
    public interface IExtensionMethodsWrapper
    {
        string MyExtension<T>(T myObj);
    }
    
    public class ExtensionMethodsWrapper : IExtensionMethodsWrapper
    {
        public string MyExtension<T>(T myObj)
        {
            return myObj.MyExtension();
        }
    }
    

    Anschließend können Sie die Wrapper-Methoden in Ihren Tests nachahmen und mit Ihrem IOC Container codieren.

    12
    ranthonissen

    Für Erweiterungsmethoden benutze ich normalerweise den folgenden Ansatz:

    public static class MyExtensions
    {
        public static Func<int,int, int> _doSumm = (x, y) => x + y;
    
        public static int Summ(this int x, int y)
        {
            return _doSumm(x, y);
        }
    }
    

    Es erlaubt _doSumm ziemlich einfach zu injizieren.

    4
    dmigo