BEARBEITEN : In Moq 4.10 können Sie jetzt einen Delegaten mit einem out- oder ref-Parameter direkt an die Callback-Funktion übergeben:
mock
.Setup(x=>x.Method(out d))
.Callback(myDelegate)
.Returns(...);
Sie müssen einen Delegaten definieren und instanziieren:
...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...
Für die Moq-Version vor 4.10:
Avner Kashtan bietet in seinem Blog eine Erweiterungsmethode an, mit der der out-Parameter aus einem Rückruf festgelegt werden kann: Moq-, Callbacks- und Out-Parameter: ein besonders schwieriger Randfall
Die Lösung ist sowohl elegant als auch hackig. Elegant, da es eine fließende Syntax bietet, die sich bei anderen Moq-Rückrufen wie zu Hause fühlt. Und hacky, weil es darauf beruht, einige interne Moq-APIs über Reflection aufzurufen.
Die unter dem obigen Link angegebene Erweiterungsmethode wurde für mich nicht kompiliert, daher habe ich unten eine bearbeitete Version bereitgestellt. Sie müssen für jede Anzahl von Eingabeparametern eine Signatur erstellen. Ich habe 0 und 1 angegeben, aber die weitere Erweiterung sollte einfach sein:
public static class MoqExtensions
{
public delegate void OutAction<TOut>(out TOut outVal);
public delegate void OutAction<in T1,TOut>(T1 arg1, out TOut outVal);
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(this ICallback<TMock, TReturn> mock, OutAction<TOut> action)
where TMock : class
{
return OutCallbackInternal(mock, action);
}
public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action)
where TMock : class
{
return OutCallbackInternal(mock, action);
}
private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(ICallback<TMock, TReturn> mock, object action)
where TMock : class
{
mock.GetType()
.Assembly.GetType("Moq.MethodCall")
.InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock,
new[] { action });
return mock as IReturnsThrows<TMock, TReturn>;
}
}
Mit der obigen Erweiterungsmethode können Sie eine Schnittstelle ohne Parameter testen, z.
public interface IParser
{
bool TryParse(string token, out int value);
}
.. mit folgendem Moq-Setup:
[TestMethod]
public void ParserTest()
{
Mock<IParser> parserMock = new Mock<IParser>();
int outVal;
parserMock
.Setup(p => p.TryParse("6", out outVal))
.OutCallback((string t, out int v) => v = 6)
.Returns(true);
int actualValue;
bool ret = parserMock.Object.TryParse("6", out actualValue);
Assert.IsTrue(ret);
Assert.AreEqual(6, actualValue);
}
Bearbeiten : Um Void-Return-Methoden zu unterstützen, müssen Sie lediglich neue Überladungsmethoden hinzufügen:
public static ICallbackResult OutCallback<TOut>(this ICallback mock, OutAction<TOut> action)
{
return OutCallbackInternal(mock, action);
}
public static ICallbackResult OutCallback<T1, TOut>(this ICallback mock, OutAction<T1, TOut> action)
{
return OutCallbackInternal(mock, action);
}
private static ICallbackResult OutCallbackInternal(ICallback mock, object action)
{
mock.GetType().Assembly.GetType("Moq.MethodCall")
.InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock, new[] { action });
return (ICallbackResult)mock;
}
Dies ermöglicht das Testen von Schnittstellen wie:
public interface IValidationRule
{
void Validate(string input, out string message);
}
[TestMethod]
public void ValidatorTest()
{
Mock<IValidationRule> validatorMock = new Mock<IValidationRule>();
string outMessage;
validatorMock
.Setup(v => v.Validate("input", out outMessage))
.OutCallback((string i, out string m) => m = "success");
string actualMessage;
validatorMock.Object.Validate("input", out actualMessage);
Assert.AreEqual("success", actualMessage);
}
It.IsAny<T>()
ähnlichen Matcher (ref It.Ref<T>.IsAny
) bis hin zur Unterstützung beim Einrichten.Callback()
und.Returns()
über benutzerdefinierte Delegattypen, die mit der Methodensignatur übereinstimmen. Geschützte Methoden werden gleichermaßen unterstützt. Siehe zB meine Antwort unten .