Zuordnung von out / ref Parameter in Moq
-
21-08-2019 - |
Frage
Ist es möglich, ein out
/ ref
Parameter mit Moq (3.0 +)?
Ich habe bei Verwendung Callback()
gesucht, aber Action<>
nicht ref Parameter unterstützen, weil es auf Generika basiert. Ich würde auch gerne vorzugsweise eine Einschränkung setzen (It.Is
) am Eingang des ref
Parameters, obwohl ich, dass in dem Callback tun kann.
Ich weiß, dass Rhino Mocks diese Funktionalität unterstützt, aber das Projekt arbeite ich an ist bereits mit Moq.
Lösung
Während die Frage nach ist Moq 3 (wahrscheinlich aufgrund seines Alters), lassen Sie mich für Moq 4.8 eine Lösung zu schreiben, die durch-ref Parameter wesentlich verbesserte Unterstützung hat.
public interface IGobbler
{
bool Gobble(ref int amount);
}
delegate void GobbleCallback(ref int amount); // needed for Callback
delegate bool GobbleReturns(ref int amount); // needed for Returns
var mock = new Mock<IGobbler>();
mock.Setup(m => m.Gobble(ref It.Ref<int>.IsAny)) // match any value passed by-ref
.Callback(new GobbleCallback((ref int amount) =>
{
if (amount > 0)
{
Console.WriteLine("Gobbling...");
amount -= 1;
}
}))
.Returns(new GobbleReturns((ref int amount) => amount > 0));
int a = 5;
bool gobbleSomeMore = true;
while (gobbleSomeMore)
{
gobbleSomeMore = mock.Object.Gobble(ref a);
}
Übrigens:. It.Ref<T>.IsAny
funktioniert auch für C # 7 in
Parameter (da sie auch durch-ref sind)
Andere Tipps
'out', die folgende scheint für mich zu arbeiten.
public interface IService
{
void DoSomething(out string a);
}
[TestMethod]
public void Test()
{
var service = new Mock<IService>();
var expectedValue = "value";
service.Setup(s => s.DoSomething(out expectedValue));
string actualValue;
service.Object.DoSomething(out actualValue);
Assert.AreEqual(expectedValue, actualValue);
}
Ich vermute, dass Moq auf den Wert von ‚expectedValue‘ aussieht, wenn Sie das Setup aufrufen und merkt es.
Für ref
, ich bin nach einer Antwort suchen auch.
Ich fand die folgende Kurzanleitung nützlich: https://github.com/Moq/moq4/wiki/Quickstart
Bearbeiten : In Moq 4.10 können Sie jetzt einen Delegierten übergeben, die einen oder ref-Parameter direkt an die Callback-Funktion hat:
mock
.Setup(x=>x.Method(out d))
.Callback(myDelegate)
.Returns(...);
Sie müssen einen Delegaten definieren und instanziiert es:
...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...
Für Moq Version vor 4.10:
Avner Kashtan bietet eine Erweiterungsmethode in seinem Blog, die die Out-Parameter von einem Rückruf ermöglicht Einstellung: Moq, Rückrufe und Out-Parameter: ein besonders heikel Rand Fall
Die Lösung ist elegant und Hacky. Elegant, dass sie eine fließend Syntax bietet, die mit anderen Moq Rückrufe zu Hause fühlt. Und Hacky weil es stützt sich auf einige interne Moq APIs über Reflektion aufrufen.
Die Extension-Methode bei der oben angegebenen Link nicht für mich zusammenstellen, also habe ich eine editierte Version weiter unten. Hier finden Sie eine Signatur für jede Anzahl von Eingabeparametern erstellen müssen Sie haben; Ich habe 0 zur Verfügung gestellt und 1, sondern erstrecken sollte es weiter sein, einfach:
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 Erweiterung Methode können Sie eine Schnittstelle mit out-Parametern testen, wie:
public interface IParser
{
bool TryParse(string token, out int value);
}
.. mit folgenden 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 : void-Rückgabeverfahren zu unterstützen, müssen Sie einfach neue Überlastung Methoden hinzuzufü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 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);
}
Dies ist die Dokumentation von Moq Website :
// out arguments
var outString = "ack";
// TryParse will return true, and the out argument will return "ack", lazy evaluated
mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true);
// ref arguments
var instance = new Bar();
// Only matches if the ref argument to the invocation is the same instance
mock.Setup(foo => foo.Submit(ref instance)).Returns(true);
Scheint, wie es nicht möglich, aus der Box ist. Sieht aus wie jemand eine Lösung
versuchtSehen Sie in diesem Forum Beitrag http://code.google.com/p/moq/issues/ Detail? id = 176
Diese Frage Überprüfen Wert der Referenzparameter mit Moq
einen Wert zurückgeben zusammen mit ref Parametereinstellung, hier ist ein Stück Code:
public static class MoqExtensions
{
public static IReturnsResult<TMock> DelegateReturns<TMock, TReturn, T>(this IReturnsThrows<TMock, TReturn> mock, T func) where T : class
where TMock : class
{
mock.GetType().Assembly.GetType("Moq.MethodCallReturn`2").MakeGenericType(typeof(TMock), typeof(TReturn))
.InvokeMember("SetReturnDelegate", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock,
new[] { func });
return (IReturnsResult<TMock>)mock;
}
}
Dann Ihre eigenen Delegierten erklären die Unterschrift zu-verspottet Methode Anpassung und Ihre eigene Methode Umsetzung.
public delegate int MyMethodDelegate(int x, ref int y);
[TestMethod]
public void TestSomething()
{
//Arrange
var mock = new Mock<ISomeInterface>();
var y = 0;
mock.Setup(m => m.MyMethod(It.IsAny<int>(), ref y))
.DelegateReturns((MyMethodDelegate)((int x, ref int y)=>
{
y = 1;
return 2;
}));
}
Dies kann eine Lösung sein.
[Test]
public void TestForOutParameterInMoq()
{
//Arrange
_mockParameterManager= new Mock<IParameterManager>();
Mock<IParameter > mockParameter= new Mock<IParameter >();
//Parameter affectation should be useless but is not. It's really used by Moq
IParameter parameter= mockParameter.Object;
//Mock method used in UpperParameterManager
_mockParameterManager.Setup(x => x.OutMethod(out parameter));
//Act with the real instance
_UpperParameterManager.UpperOutMethod(out parameter);
//Assert that method used on the out parameter of inner out method are really called
mockParameter.Verify(x => x.FunctionCalledInOutMethodAfterInnerOutMethod(),Times.Once());
}
kämpfte ich mit vielen der Vorschläge hier, bevor ich einfach eine Instanz einer neuen ‚Fälschung‘ Klasse geschaffen, was Schnittstelle implementiert Sie zu verspotten versuchen. Dann können Sie einfach den Wert der Out-Parameter mit der Methode selbst.
ich damit zu kämpfen an diesem Nachmittag für eine Stunde und könnte eine Antwort nicht überall finden. Nach dem Spielen mit ihm auf meinem eigenen um konnte ich mit einer Lösung kommen, die für mich gearbeitet.
string firstOutParam = "first out parameter string";
string secondOutParam = 100;
mock.SetupAllProperties();
mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);
Der Schlüssel hier ist mock.SetupAllProperties();
, die alle Eigenschaften für Dich Stub werden. Dies kann in jedem Testfall-Szenario nicht funktionieren, aber wenn alles kümmern Sie ist die return value
von YourMethod
bekommen, dann wird dies gut funktionieren.