Pregunta

¿Es posible asignar un parámetro out / ref usando Moq (3.0 +)?

He mirado en el uso de Callback(), pero Action<> no apoya parámetros árbitro porque se basa en los genéricos. También me gustaría preferiblemente para poner una restricción (It.Is) en la entrada de la <=> parámetro, aunque puede hacer que en la devolución de llamada.

Sé que burla de Rhino soporta esta funcionalidad, pero el proyecto que estoy trabajando ya está utilizando Moq.

¿Fue útil?

Solución

Mientras que la pregunta es sobre Moq 3 (probablemente debido a su edad), me permite publicar una solución para Moq 4.8, que tiene mucho mejor soporte para los parámetros de 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<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 trabaja para C # 7 in parámetros (ya que también son por-ref)

Otros consejos

Para 'hacia fuera', la siguiente parece que funciona 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(expectedValue, actualValue);
}

supongo que Moq mira el valor de 'expectedValue' cuando se llama el programa de instalación y lo recuerda.

Para ref, estoy buscando una respuesta también.

he encontrado la siguiente guía de inicio rápido útil: https://github.com/Moq/moq4/wiki/Quickstart

Editar : En Moq 4.10, ahora puede pasar a un delegado que tiene una salida o parámetro ref directamente a la función de devolución de llamada:

mock
  .Setup(x=>x.Method(out d))
  .Callback(myDelegate)
  .Returns(...); 

Se tendrá que definir un delegado y instanciarlo:

...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...

Para la versión 4.10 antes de Moq:

Avner Kashtan proporciona un método de extensión en su blog que permite establecer el parámetro a partir de una devolución de llamada: Moq, Callbacks y de salida parámetros: un caso borde particularmente difícil

La solución es elegante y hacky. Elegante, ya que proporciona una sintaxis fluida que se siente en casa con otros servicios repetidos Moq. Y hacky porque se basa en llamar a algunas API internas Moq través de la reflexión.

El método de extensión proporcionada en el enlace de arriba no se compila para mí, así que he proporcionado una versión editada de abajo. Tendrá que crear una firma para cada número de parámetros de entrada que tiene; He proporcionado 0 y 1, pero extendiéndose más lejos 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, se puede probar una interfaz con parámetros out tales como:

public interface IParser
{
    bool TryParse(string token, out int value);
}

.. con la siguiente configuración 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);
    }


Editar : Para apoyar métodos nula-retorno, sólo hay que añadir 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 que las interfaces de pruebas tales 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 de sitio 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);

Parece que no es posible fuera de la caja. Parece que alguien ha intentado una solución

Vea este mensaje en el foro http://code.google.com/p/moq/issues/ detalle? id = 176

esta pregunta Verificar valor de parámetro de referencia con Moq

Para devolver un valor, junto con el establecimiento de parámetros ref, aquí es una pieza 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;
    }
}

A continuación, declarar su propio juego delegado de la firma del método a-ser-burlado y proporcionar 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;
         }));
    }

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 muchas de las sugerencias aquí antes sencilla creado una instancia de una nueva clase 'falso' que implementa la interfaz de lo que usted está tratando de burlarse a cabo. A continuación, puede establecer el valor del parámetro con el método en sí.

Luché con esto durante una hora esta tarde y no pude encontrar una respuesta en cualquier lugar. Después de jugar por mi cuenta con que yo era capaz de llegar a 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(); que apagar todas las propiedades para usted. Esto puede no funcionar en todos los casos de prueba, pero si todo lo que importa es conseguir que el return value de YourMethod entonces esto va a funcionar bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top