如何使用最小起订量更改回调中的值?
How can I change out value in callback using Moq?
我正在尝试在 UnitTest 中模拟一些第 3 方库。它将结果数据放在 out
参数中,而 returns 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
我该怎么做?
看起来不支持开箱即用的输出参数更改。目前我找到的最佳解决方案是基于 hack - 参见 。它使用反射调用私有方法。
使用该 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>;
}
}
是的,似乎 out
参数在一开始只使用了一次,并且连续调用无法更改它。 return 值可以像 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
内部被更改的次数与从 while
调用 Query
方法的次数一样多,但这并不影响out
参数值仍然是 'first'。
outString from callback
second
inside while
first
outString from callback
third
inside while
first
outString from callback
fourth
after while
first
我正在尝试在 UnitTest 中模拟一些第 3 方库。它将结果数据放在 out
参数中,而 returns 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
我该怎么做?
看起来不支持开箱即用的输出参数更改。目前我找到的最佳解决方案是基于 hack - 参见 。它使用反射调用私有方法。 使用该 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>;
}
}
是的,似乎 out
参数在一开始只使用了一次,并且连续调用无法更改它。 return 值可以像 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
内部被更改的次数与从 while
调用 Query
方法的次数一样多,但这并不影响out
参数值仍然是 'first'。
outString from callback
second
inside while
first
outString from callback
third
inside while
first
outString from callback
fourth
after while
first