我正在尝试在UnitTest中模拟一些第三方库。如果有更多数据可用,它会将结果数据放在out
参数中,同时返回bool
信号。我想测试我的组件在有两页数据时表现良好,但无法弄清楚如何在Setup()
方法中更改out参数中的数据。我创建了可以在Linqpad中运行的极简主义示例:
void Main()
{
var m = new Mock<IFoo>();
string s = "1";
var pg = 0;
m.Setup(o => o.Query(out s))
.Returns(() => pg==0)
.Callback(() => { pg++; s = "2"; });
IFoo f = m.Object;
string z;
while (f.Query(out z))
{
z.Dump();
}
z.Dump();
}
public interface IFoo {
bool Query(out string result);
}
输出
1
1
我该怎么做?
答案 0 :(得分:1)
看起来开箱即用不支持更改输出参数。到目前为止,我找到的最佳解决方案是基于黑客攻击 - 请参阅https://stackoverflow.com/a/19598345/463041。它使用反射调用私有方法。 使用该hack,工作解决方案将如下所示:
void Main()
{
var m = new Mock<IFoo>();
string s = "1";
var pg = 0;
m.Setup(p => p.Query(out s))
.OutCallback((out string v) => v = pg==0 ? "1" : "2")
.Returns(() => pg==0)
.Callback(() => { pg++; s = "2"; });
IFoo f = m.Object;
string z;
while (f.Query(out z))
{
z.Dump();
}
z.Dump();
}
public interface IFoo {
bool Query(out string result);
}
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>;
}
}
答案 1 :(得分:0)
是的,似乎out
参数一开始只使用一次,而后续调用无法更改它。可以像描述here那样修改返回值,但out
参数不能修改。
var outResults = new Queue<string>(new[] { "first","second","third","fourth" });
string outString = outResults.Dequeue();
m.Setup(o => o.Query(out outString))
.Callback(() =>
{
outString = outResults.Dequeue();
outString.Dump("outString from callback");
})
.Returns(new Queue<bool>(new[] { true, true, false }).Dequeue);
IFoo f = m.Object;
string z;
while (f.Query(out z))
{
z.Dump("inside while");
}
z.Dump("after while");
outString
内callback
实际上已经从Query
调用while
方法的次数发生了多次变化,但这并没有影响到out
参数哪个值仍然是“第一个”&#39;。
outString from callback
second
inside while
first
outString from callback
third
inside while
first
outString from callback
fourth
after while
first