EDIT : No Moq 4.10, agora você pode passar um delegado que possui um parâmetro out ou ref diretamente para a função de retorno de chamada:
mock
.Setup(x=>x.Method(out d))
.Callback(myDelegate)
.Returns(...);
Você precisará definir um delegado e instancia-lo:
...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...
Para a versão Moq anterior à 4.10:
Avner Kashtan fornece um método de extensão em seu blog que permite definir o parâmetro out de um retorno de chamada: parâmetros Moq, Callbacks e Out: um caso particularmente delicado
A solução é elegante e hacky. Elegante, pois fornece uma sintaxe fluente que se sente em casa com outros retornos de chamada do Moq. E hacky porque depende de chamar algumas APIs Moq internas por meio de reflexão.
O método de extensão fornecido no link acima não foi compilado para mim, por isso forneci uma versão editada abaixo. Você precisará criar uma assinatura para cada número de parâmetros de entrada que você possui; Forneci 0 e 1, mas estendê-lo ainda mais 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 sem parâmetros como:
public interface IParser
{
bool TryParse(string token, out int value);
}
.. com a seguinte configuração do 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 oferecer suporte a métodos de retorno nulo, basta 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 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);
}
It.IsAny<T>()
matcher-like (ref It.Ref<T>.IsAny
) a suporte para configuração.Callback()
e.Returns()
por meio de tipos de delegados personalizados que correspondem à assinatura do método. Métodos protegidos são igualmente suportados. Veja, por exemplo, minha resposta abaixo .