Das vollständig generische Moq-Setup mit einem Klassen-Proxy-Versuch löst die Meldung aus, dass spät gebundene Vorgänge
Posted: 03 Jan 2025, 07:19
Zu lösendes Problem
Ich versuche, mithilfe der Moq-Bibliothek eine Nachbildung der Schnittstelle ISomeService zu erstellen, die ihre eigentliche Implementierung SomeService umschließt< /code>.
Im Moment mache ich das manuell so:
Es ruft eine echte Implementierung auf, sodass meine Tests zuverlässiger sind und ich von einem Mock wie Verify usw. profitiere. Das Problem ist, dass ich es für jede Methode und jeden Dienst implementieren muss, den ich verwende nicht der richtige Weg. Ich strebe eine vollautomatische Lösung an.
WAS ICH GEMACHT HABE
Was ich für Setup() habe Teil ist
Und jetzt der lustige Teil. Wenn ich Lambda so definiere:
Es erzeugt einen LambdaExpression und funktioniert nicht mit der Setup-Methodendefinition, die einen vollständig definierten Ausdruck erfordert:
Ich kann Setup nicht direkt aufrufen, da ich den Typ von TResult nicht kenne, also greife ich offensichtlich auf Reflektion zurück:
aber ich erhalte eine Fehlermeldung
System.InvalidOperationException: „Spät gebundene Vorgänge können nicht für Typen oder Methoden ausgeführt werden.“ which ContainsGenericParameters is true.'
was Sinn macht, aber enttäuschend ist.
WAS ICH VERSUCHT HABE< /strong>
Ich habe versucht, das Ergebnis zu übertragen Typ zu Objekt Typ
damit ich schreiben konnte
aber dies löst auch eine Ausnahme aus
System.ArgumentException: 'Unsupported expression: (object)x.Run( It.IsAny())'
Damit dies funktioniert, habe ich eine Reihe von else if-Anweisungen für Werttypen hinzugefügt, bei denen ich für einen bestimmten Ergebnistyp einen bestimmten Ausdruck zurückgebe aber das Es ist ein Albtraum, es aufrechtzuerhalten und zu erweitern. Für Klassen verwende ich Objekt und zumindest dieser Teil funktioniert.
Für benutzerdefinierte Werttypen habe ich eine generische Methode hinzugefügt
und der Benutzer muss das fehlende Setup selbst hinzufügen.
FRAGE[/b]
Gibt es eine Möglichkeit, diesen Code vollständig generisch zu gestalten? Vielleicht gibt es einen besseren Weg, dieses Problem zu lösen? Oder muss ich mich an diese fiese Sonst-wenn-Lösung halten?
Ich versuche, mithilfe der Moq-Bibliothek eine Nachbildung der Schnittstelle ISomeService zu erstellen, die ihre eigentliche Implementierung SomeService umschließt< /code>.
Im Moment mache ich das manuell so:
Code: Select all
var instance = new SomeService();
var mock = new Mock();
mock.Setup(x => x.Run(It.IsAny()))
.Returns(x => instance.Run(x));
WAS ICH GEMACHT HABE
Was ich für Setup() habe Teil ist
Code: Select all
// (x)
var mockParameter = Expression.Parameter(typeof(T), "x");
// It.IsAny()
var isAnyMethod = typeof(It).GetMethod(nameof(It.IsAny));
var isAny = parameters.Select(x => Expression.Call(isAnyMethod.MakeGenericMethod(x.ParameterType))).ToArray();
// x.Run(It.IsAny())
mockCall = Expression.Call(mockParameter, method, isAny);
Code: Select all
// (x) => x.Run(It.IsAny())
var mockLambda = Expression.Lambda(mockCall, mockParameter);
Code: Select all
public ISetup Setup(Expression expression)
Code: Select all
var setupMethod = typeof(Mock)
.GetMethods()
.Where(x => x.Name == nameof(mock.Setup) && x.GetParameters()[0].ParameterType.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(Func)).First()
.MakeGenericMethod(method.ReturnType);
var setupResult = setupMethod.Invoke(mock, new object[] { mockLambda });
System.InvalidOperationException: „Spät gebundene Vorgänge können nicht für Typen oder Methoden ausgeführt werden.“ which ContainsGenericParameters is true.'
was Sinn macht, aber enttäuschend ist.
WAS ICH VERSUCHT HABE< /strong>
Ich habe versucht, das Ergebnis zu übertragen Typ zu Objekt Typ
Code: Select all
var converted = Expression.Convert(mockCall, typeof(object));
Code: Select all
// (x) => (object)x.Run(It.IsAny())
var mockLambda = Expression.Lambda(converted, mockParameter);
System.ArgumentException: 'Unsupported expression: (object)x.Run( It.IsAny())'
Damit dies funktioniert, habe ich eine Reihe von else if-Anweisungen für Werttypen hinzugefügt, bei denen ich für einen bestimmten Ergebnistyp einen bestimmten Ausdruck zurückgebe aber das Es ist ein Albtraum, es aufrechtzuerhalten und zu erweitern. Für Klassen verwende ich Objekt und zumindest dieser Teil funktioniert.
Für benutzerdefinierte Werttypen habe ich eine generische Methode hinzugefügt
Code: Select all
void SetupUnsuportedMethod(MethodInfo methodInfo)
FRAGE[/b]
Gibt es eine Möglichkeit, diesen Code vollständig generisch zu gestalten? Vielleicht gibt es einen besseren Weg, dieses Problem zu lösen? Oder muss ich mich an diese fiese Sonst-wenn-Lösung halten?