È ansible assegnare un parametro out
/ ref
usando Moq (3.0+)?
Ho cercato di usare Callback()
, ma Action
non supporta i parametri ref perché è basato su generici. Preferirei preferibilmente inserire un vincolo ( It.Is
) sull’input del parametro ref
, sebbene possa farlo nel callback.
So che Rhino Mocks supporta questa funzionalità, ma il progetto su cui sto lavorando utilizza già Moq.
Sebbene la domanda riguardi il Moq 3 (probabilmente a causa della sua età), permettimi di pubblicare una soluzione per Moq 4.8, che ha un supporto molto migliorato per i parametri by-ref.
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(); mock.Setup(m => m.Gobble(ref It.Ref.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); }
A proposito: It.Ref
funziona anche con C # 7 in
parametri (dato che sono anche di riferimento).
Per “out”, il seguente sembra funzionare per me.
public interface IService { void DoSomething(out string a); } [TestMethod] public void Test() { var service = new Mock(); var expectedValue = "value"; service.Setup(s => s.DoSomething(out expectedValue)); string actualValue; service.Object.DoSomething(out actualValue); Assert.AreEqual(actualValue, expectedValue); }
Sto indovinando che Moq guarda il valore di ‘expectedValue’ quando chiamate Setup e lo memorizza.
Per ref
, sto cercando anche una risposta.
Ho trovato utile la seguente guida di QuickStart: https://github.com/Moq/moq4/wiki/Quickstart
Avner Kashtan fornisce un metodo di estensione nel suo blog che consente di impostare il parametro out da un callback: Moq, Callbacks e Out parameters: un caso limite particolarmente complicato
La soluzione è sia elegante che hacky. Elegante in quanto fornisce una syntax fluente che si sente a casa con altri callback Moq. E hacky perché si basa sulla chiamata di alcune API Moq interne tramite riflessione.
Il metodo di estensione fornito nel link precedente non è stato compilato per me, quindi ho fornito una versione modificata di seguito. Dovrai creare una firma per ogni numero di parametri di input che hai; Ho fornito 0 e 1, ma estenderlo ulteriormente dovrebbe essere semplice:
public static class MoqExtensions { public delegate void OutAction(out TOut outVal); public delegate void OutAction(T1 arg1, out TOut outVal); public static IReturnsThrows OutCallback(this ICallback mock, OutAction action) where TMock : class { return OutCallbackInternal(mock, action); } public static IReturnsThrows OutCallback(this ICallback mock, OutAction action) where TMock : class { return OutCallbackInternal(mock, action); } private static IReturnsThrows OutCallbackInternal(ICallback 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; } }
Con il metodo di estensione sopra, puoi testare un’interfaccia con parametri quali:
public interface IParser { bool TryParse(string token, out int value); }
.. con la seguente configurazione Moq:
[TestMethod] public void ParserTest() { Mock parserMock = new Mock (); 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); }
Modifica : per supportare i metodi void-return, è sufficiente aggiungere nuovi metodi di overload:
public static ICallbackResult OutCallback(this ICallback mock, OutAction action) { return OutCallbackInternal(mock, action); } public static ICallbackResult OutCallback(this ICallback mock, OutAction 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; }
Ciò consente di testare interfacce come:
public interface IValidationRule { void Validate(string input, out string message); } [TestMethod] public void ValidatorTest() { Mock validatorMock = new Mock (); 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); }
Questa è la documentazione dal sito Moq :
// 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);
Sembra che non sia ansible fuori dalla scatola. Sembra che qualcuno abbia tentato una soluzione
Vedi questo post del forum http://code.google.com/p/moq/issues/detail?id=176
questa domanda Verificare il valore del parametro di riferimento con Moq
Questa può essere una soluzione.
[Test] public void TestForOutParameterInMoq() { //Arrange _mockParameterManager= new Mock(); Mock mockParameter= new Mock (); //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()); }
Per restituire un valore insieme all’impostazione del parametro ref, ecco un pezzo di codice:
public static class MoqExtensions { public static IReturnsResult DelegateReturns(this IReturnsThrows 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 )mock; } }
Quindi dichiarare il proprio delegato che corrisponde alla firma del metodo to-be-mocked e fornire la propria implementazione del metodo.
public delegate int MyMethodDelegate(int x, ref int y); [TestMethod] public void TestSomething() { //Arrange var mock = new Mock(); var y = 0; mock.Setup(m => m.MyMethod(It.IsAny(), ref y)) .DelegateReturns((MyMethodDelegate)((int x, ref int y)=> { y = 1; return 2; })); }
Ho lottato con questo per un’ora questo pomeriggio e non sono riuscito a trovare una risposta da nessuna parte. Dopo aver giocato da solo con esso sono stato in grado di trovare una soluzione che ha funzionato per me.
string firstOutParam = "first out parameter string"; string secondOutParam = 100; mock.SetupAllProperties(); mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);
La chiave qui è mock.SetupAllProperties();
che cancellerà tutte le proprietà per te. Questo potrebbe non funzionare in ogni scenario di test, ma se tutto quello che ti interessa è ottenere il return value
di return value
di YourMethod
allora funzionerà YourMethod
.