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:
Code: Select all
var instance = new SomeService();
var mock = new Mock();
mock.Setup(x => x.Run(It.IsAny()))
.Returns(x => instance.Run(x));
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
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);
Und jetzt der lustige Teil. Wenn ich Lambda so definiere:
Code: Select all
// (x) => x.Run(It.IsAny())
var mockLambda = Expression.Lambda(mockCall, mockParameter);
Es erzeugt einen LambdaExpression und funktioniert nicht mit der Setup-Methodendefinition, die einen vollständig definierten Ausdruck erfordert:
Code: Select all
public ISetup Setup(Expression expression)
Ich kann Setup nicht direkt aufrufen, da ich den Typ von TResult nicht kenne, also greife ich offensichtlich auf Reflektion zurück:
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 });
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
Code: Select all
var converted = Expression.Convert(mockCall, typeof(object));
damit ich schreiben konnte
Code: Select all
// (x) => (object)x.Run(It.IsAny())
var mockLambda = Expression.Lambda(converted, mockParameter);
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
Code: Select all
void SetupUnsuportedMethod(MethodInfo methodInfo)
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?