开箱即用不支持为 ref 参数指定行为,但是可以添加您自己的扩展方法来支持此行为。这里发布了一个解决方案。
对于您的示例,测试看起来有点像这样:
[TestClass]
public class RefUnitTests
{
private Queue<int> queue = new Queue<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
[TestMethod]
public void TestRefArgs()
{
int inputInt = 0;
var repository = new Mock<IRepository>();
repository
.Setup(r => r.Create(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), ref inputInt))
.Returns(0)
.IgnoreRefMatching()
.RefCallback<string, int, int, int, int, IRepository>(new MoqExtension.RefAction<string, int, int, int, int>(ReportInfoRefCallBackAction));
int actualInt = -1;
for (int i = 1; i <= 10; i++)
{
repository.Object.Create(string.Empty, 1, 2, 3, ref actualInt);
// Assert that we are really dequeuing items
Assert.AreEqual(i, actualInt);
}
}
public void ReportInfoRefCallBackAction(string a, int b, int c, int d, ref int refInt)
{
// You can also assert on the incoming ref value here if you wish, I wrote
// it to the console so that you can see it is changing.
Console.WriteLine(refInt);
refInt = queue.Dequeue();
}
}
您没有定义要测试的存储库,所以我使用了一个看起来相似的界面:
public interface IRepository
{
int Create(string arg1, int arg2, int arg3, int arg4, ref int arg);
}
这是扩展 Moq 所需的代码,改编自上面的链接:
public static class MoqExtension
{
public delegate void RefAction<TParam1, TParam2, TParam3, TParam4, TRef>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, ref TRef refVal1);
public static IReturnsResult<TMock> RefCallback<TParam1, TParam2, TParam3, TParam4, TRef, TMock>(
this ICallback mock,
RefAction<TParam1, TParam2, TParam3, TParam4, TRef> action) where TMock : class
{
mock.GetType().Assembly
.GetType("Moq.MethodCall")
.InvokeMember("SetCallbackWithArguments",
BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
null,
mock,
new object[] { action });
return mock as IReturnsResult<TMock>;
}
public static ICallback IgnoreRefMatching(this ICallback mock)
{
try
{
FieldInfo matcherField = typeof(Mock).Assembly.GetType("Moq.MethodCall").GetField("argumentMatchers", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.Instance);
IList argumentMatchers = (IList)matcherField.GetValue(mock);
Type refMatcherType = typeof(Mock).Assembly.GetType("Moq.Matchers.RefMatcher");
FieldInfo equalField = refMatcherType.GetField("equals", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.Instance);
foreach (object matcher in argumentMatchers)
{
if (matcher.GetType() == refMatcherType)
equalField.SetValue(matcher, new Func<object, bool>(delegate(object o) { return true; }));
}
return mock;
}
catch (NullReferenceException)
{
return mock;
}
}
}