106 Stimmen

Wie verwende ich Moq, um eine Erweiterungsmethode nachzubilden?

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. Mocking das Ergebnis schien die offensichtliche Wahl, aber Moq scheint keine Möglichkeit zu bieten, eine statische Methode zu überschreiben (eine Voraussetzung für eine Erweiterungsmethode). Es gibt eine ähnliche Idee mit Moq.Protected und Moq.Stub, aber sie scheinen nichts für dieses Szenario zu bieten. Übersehe ich etwas oder sollte ich das anders angehen?

Hier ist ein triviales Beispiel, das mit der üblichen "Ungültige Erwartung an ein nicht überschreibbares Mitglied" . Dies ist ein schlechtes Beispiel für die Notwendigkeit, eine Erweiterungsmethode nachzubilden, 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 });

Wie für alle TypeMock-Junkies, die vorschlagen, dass ich stattdessen Isolator verwenden könnte: Ich weiß die Mühe zu schätzen, denn es sieht so aus, als könnte TypeMock die Aufgabe mit verbundenen Augen und im Rausch erledigen, aber unser Budget wird in nächster Zeit nicht steigen.

3 Stimmen

Ein Duplikat ist hier zu finden: stackoverflow.com/questions/2295960/ .

7 Stimmen

Diese Frage ist ein ganzes Jahr älter als jene. Wenn es Überschneidungen gibt, ist es andersherum.

2 Stimmen

Auch 2019 keine angemessene Lösung!

89voto

Mendelt Punkte 35649

Erweiterungsmethoden sind nur statische Methoden in Verkleidung. Mocking-Frameworks wie Moq oder Rhinomocks können nur Mock-Instanzen von Objekten erstellen, d. h. das Mocking statischer Methoden ist nicht möglich.

79 Stimmen

@Mendelt Wie würde man dann eine Methode testen, die intern eine Erweiterungsmethode hat? Was sind die möglichen Alternativen?

6 Stimmen

@Alexander Ich hatte die gleiche Frage und das hier hat sie fantastisch beantwortet: agooddayforscience.blogspot.com/2017/08/ -Schauen Sie sich das an, es ist ein Lebensretter!

42voto

informatorius Punkte 566

Wenn Sie den Code der Erweiterungsmethoden ändern können, dann können Sie ihn so codieren, dass Sie ihn testen 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 syntaktischer Zucker sind).

Und Sie können die Implementierungsschnittstelle nachbilden und sie 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 myClassUsingExtensions = new MyClassUsingExtensions();

        // When:
        //-------
        var myObject = new Object();
        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));
    }
}

17voto

Mike Fielden Punkte 9905

Ich weiß, dass diese Frage seit etwa einem Jahr nicht mehr aktiv ist, aber Microsoft hat ein Framework herausgegeben, das genau dies ermöglicht. Maulwürfe .

Auch hier gibt es ein paar Anleitungen:

16voto

ranthonissen Punkte 1435

Ich habe eine Wrapper-Klasse für die Erweiterungsmethoden erstellt, die ich nachahmen musste.

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

Dann können Sie die Wrapper-Methoden in Ihren Tests und im Code mit Ihrem IOC-Container nachbilden.

0 Stimmen

Dies ist ein Workaround, bei dem Sie die Syntax der Erweiterungsmethoden nicht mehr in Ihrem Code verwenden können. Aber es hilft, wenn Sie die Klasse der Erweiterungsmethoden nicht ändern können.

0 Stimmen

@informatorius Was meinen Sie damit? In Ihrem Code verwenden Sie MyExtension() aus der Klasse MyExtensions. In Ihren Tests verwenden Sie MyExtension() aus der Klasse ExtensionMethodsWrapper(), die den Parameter bereitstellt.

0 Stimmen

Wenn meine zu testende Klasse MyExtension() aus den MyExtensions verwendet, kann ich die MyExtensions nicht nachahmen. Also muss die zu testende Klasse den IExtensionMethodsWrapper verwenden, damit sie gespottet werden kann. Aber dann kann die zu testende Klasse die Syntax der Erweiterungsmethoden nicht mehr verwenden.

4voto

dmigo Punkte 2556

Für Erweiterungsmethoden verwende 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 relativ einfach zu injizieren.

2 Stimmen

Ich habe dies gerade versucht, aber es gibt Probleme bei der Übergabe des Parent mocked Objekts, dass die Erweiterung ist für bei der Übergabe in eine andere Methode, die in einer anderen Assembly ist. In diesem Fall sogar mit dieser Technik die ursprüngliche Erweiterung wird ausgewählt, wenn die Methode in der anderen Assembly aufgerufen wird, eine Art von Scoping-Problem. Wenn ich direkt im Unit-Test-Projekt aufrufe, ist es zwar in Ordnung, aber wenn Sie anderen Code testen, der die Erweiterungsmethode aufruft, ist es einfach nicht hilfreich.

0 Stimmen

@Stephen Ich weiß nicht, wie man das vermeiden kann. Ich hatte noch nie diese Art von Scoping-Problem

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X