c# - generate - Asignando parámetros/ref en Moq
params comments c# (9)
¿Es posible asignar un parámetro de out
/ ref
utilizando Moq (3.0+)?
He analizado el uso de Callback()
, pero Action<>
no admite parámetros de referencia porque se basa en genéricos. También me gustaría, preferiblemente, poner una restricción ( It.Is
) en la entrada del parámetro ref
, aunque puedo hacerlo en la devolución de llamada.
Sé que Rhino Mocks es compatible con esta funcionalidad, pero el proyecto en el que estoy trabajando ya está usando Moq.
Avner Kashtan proporciona un método de extensión en su blog que permite configurar el parámetro out a partir de una devolución de llamada: Moq, Callbacks y Out parámetros: un caso de borde particularmente complicado
La solución es a la vez elegante y hacky. Elegante en el sentido de que proporciona una sintaxis fluida que se siente como en casa con otras devoluciones de llamada Moq. Y hacky porque se basa en llamar a algunas API internas de Moq a través de la reflexión.
El método de extensión provisto en el enlace anterior no se compiló para mí, por lo que proporcioné una versión editada a continuación. Deberá crear una firma para cada número de parámetros de entrada que tenga; He proporcionado 0 y 1, pero extenderlo más debería ser simple:
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>;
}
}
Con el método de extensión anterior, puede probar una interfaz sin parámetros tales como:
public interface IParser
{
bool TryParse(string token, out int value);
}
.. con la siguiente configuración de Moq:
[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);
}
Edición : para admitir los métodos de devolución de vacíos, simplemente necesita agregar nuevos métodos de sobrecarga:
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;
}
Esto permite probar interfaces como:
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);
}
Esta es la documentación del code.google.com/p/moq/wiki/QuickStart :
// 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);
Esto puede ser una solución.
[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());
}
Luché con esto durante una hora esta tarde y no pude encontrar una respuesta en ninguna parte. Después de jugar solo con él, pude encontrar una solución que funcionó para mí.
string firstOutParam = "first out parameter string";
string secondOutParam = 100;
mock.SetupAllProperties();
mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);
La clave aquí es mock.SetupAllProperties();
el cual eliminará todas las propiedades para ti. Es posible que esto no funcione en todos los casos de prueba, pero si lo único que le importa es obtener el return value
de return value
de YourMethod
entonces esto funcionará bien.
Luché con muchas de las sugerencias aquí antes de que simplemente creara una instancia de una nueva clase ''Fake'' que implementa cualquier interfaz que esté intentando simular. Luego, simplemente puede establecer el valor del parámetro out con el método en sí.
Para ''out'', lo siguiente parece funcionar para mí.
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(actualValue, expectedValue);
}
Supongo que Moq mira el valor de ''expectedValue'' cuando llama a Configuración y lo recuerda.
Para ref
, estoy buscando una respuesta también.
Encontré útil la siguiente guía de inicio rápido: https://github.com/Moq/moq4/wiki/Quickstart
Para devolver un valor junto con la configuración del parámetro ref, aquí hay un fragmento de código:
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;
}
}
Luego declare su propio delegado que coincida con la firma del método to-be-be-mocked y proporcione su propia implementación del método.
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;
}));
}
Parece que no es posible salir de la caja. Parece que alguien intentó una solución
Consulte la publicación de este foro http://code.google.com/p/moq/issues/detail?id=176
esta pregunta Verifique el valor del parámetro de referencia con Moq
Si bien la pregunta es sobre Moq 3 (probablemente debido a su edad), permítame publicar una solución para Moq 4.8, que tiene un soporte mucho mayor para los parámetros de referencia.
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);
}
Por cierto: It.Ref<T>.IsAny
también funciona para C # 7 in
parámetros (ya que también son by-ref).