Assegnazione dei parametri di / ref in Moq

È 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.IsAny 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 .