Pergunta

É possível atribuir um parâmetro out / ref usando Moq (3.0 +)?

Eu olhei usando Callback(), mas Action<> não suporta parâmetros ref porque ele é baseado em genéricos. Eu também, de preferência, como colocar uma restrição (It.Is) na entrada do parâmetro ref, embora eu posso fazer isso no retorno de chamada.

Eu sei que Rhino Mocks suporta esta funcionalidade, mas o projeto que estou trabalhando já está usando Moq.

Foi útil?

Solução

Embora a pergunta é sobre Moq 3 (provavelmente devido a sua idade), permita-me para postar uma solução para Moq 4.8, que tem muito suporte melhorado para os 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);
}

A propósito:. It.Ref<T>.IsAny também funciona para C # 7 parâmetros in (desde que eles também são by-ref)

Outras dicas

para 'out', o seguinte parece funcionar para mim.

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);
}

Eu estou supondo que a aparência Moq no valor de 'expectedValue' quando você chamar de configuração e se lembra dele.

Para ref, eu estou procurando uma resposta também.

Eu encontrei o seguinte guia QuickStart útil: https://github.com/Moq/moq4/wiki/Quickstart

Editar : Em Moq 4,10, agora você pode passar um delegado que tem um out ou ref parâmetro diretamente para a função de chamada de retorno:

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

Você terá que definir um delegado e instanciá-lo:

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

Para a versão Moq antes 4.10:

Avner Kashtan fornece um método de extensão em seu blog que permite definir o parâmetro a partir de uma chamada de retorno: parâmetros Moq, Callbacks e out: um caso extremo particularmente complicado

A solução é elegante e hacky. Elegante na medida em que fornece uma sintaxe fluente que se sente em casa com outras chamadas de retorno Moq. E hacky porque depende de chamar alguns Moq APIs interno através da reflexão.

O método de extensão fornecido no link acima não compilar para mim, então eu forneci uma versão editada abaixo. Você vai precisar para criar uma assinatura para cada número de parâmetros de entrada que você tem; Eu forneci 0 e 1, mas estendê-lo ainda deve ser simples:

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>;
    }
}

Com o método de extensão acima, você pode testar uma interface com os parâmetros, tais como:

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

.. com a seguinte configuração 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 : Apoiar métodos vazio e retorno, você simplesmente precisa adicionar novos 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;
}

Isso permite testar as interfaces, tais 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 é a documentação do Moq local :

// 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 não é possível fora da caixa. Parece que alguém tentou uma solução

Veja este post no fórum http://code.google.com/p/moq/issues/ detalhe? id = 176

Esta questão Verifique o valor do parâmetro de referência com Moq

Para retornar um valor, juntamente com a criação ref parâmetro, aqui é um pedaço 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;
    }
}

Em seguida, declarar a sua própria delegado combinando a assinatura do método a ser-escarnecido e fornecer sua própria implementação do 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;
         }));
    }

Esta pode ser uma solução.

[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());

}

Eu lutava com muitas das sugestões aqui antes de eu simples criado uma instância da classe um novo 'Falso' que implementa qualquer que seja a relação que você está tentando zombar fora. Então você pode simplesmente definir o valor do parâmetro com o próprio método.

Eu lutei com isso por uma hora nesta tarde e não poderia encontrar um lugar resposta. Depois de brincar sozinha com ele eu era capaz de chegar a uma solução que funcionou para mim.

string firstOutParam = "first out parameter string";
string secondOutParam = 100;
mock.SetupAllProperties();
mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);

A chave aqui é mock.SetupAllProperties(); que irá stub todas as propriedades para você. Isso pode não funcionar em todos os cenários caso de teste, mas se tudo o que importa é conseguir o return value de YourMethod então este trabalho bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top