2012-11-09 76 views
2

我有一个方法,我正在测试哪个需要'ref'参数。我希望这个参数在每次调用方法时都返回一个不同的值(我预计在这个测试中会有10次)。我想不出如何嘲笑这一点。以下是我迄今为止 - 这不会编译:Moq,ref,设置和队列

var refParentMenuId = It.Is<int>(i => new Queue<int>(new int [] { 1,2,3,4,5,6,7,8,9,10 }).Dequeue); 

this.MockMenuRepository.Setup(m => m.Create(It.IsAny<string>, It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), ref refParentMenuId)); 

我想利用裁判,而不是返回一个结构,我觉得这种方式更直观地坚持下去。

回答

3

为ref参数指定的行为是not supported out of the box,但是可以添加您自己的扩展方法来支持此行为。有一个解决方案发布here

为了您例如,测试看起来有点像这样:

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