title: How can I write a generic method that sets up a Moq object expectation?
body: <p>Method M takes 2 parameters, P1 and P2. P2 is a delegate. I want to tell a mock object, "Whenever method M is called with parameter P1, invoke P2 and pass object O to it." I'm using Moq.</p>
  
  <p>The following approach works, but seems a bit verbose.</p>
  
  <pre><code>this.DataCacheMock = Mock.Of&lt;IDataCache&gt;();
  var dataObject = new DataObject();
  
  Mock.Get(this.DataCacheMock)
  .Setup(m =&gt; m.GetDataObject(123, It.IsAny&lt;EventHandler&lt;DataPortalResult&lt;DataObject&gt;&gt;&gt;()))
  .Callback((int id, EventHandler&lt;DataPortalResult&lt;DataObject&gt;&gt; callback) =&gt; callback(null, new DataPortalResult(dataObject, null, null)));
  </code></pre>
  
  <p>I'd like to refactor that last bit into a generic helper method so that I (and future test authors) would only need to write something like this:</p>
  
  <pre><code>TestTools.ArrangeDataPortalResult(this.DataCacheMock.GetDataObject, 123, dataObject);
  </code></pre>
  
  <p>The big question is: what would go inside that helper method? I've had partial success so far, but I'm wondering if there's any way to get all the way there.</p>
  
  <h2>First Attempt (doesn't work)</h2>
  
  <pre><code>public static void ArrangeDataPortalResult&lt;TMock, TResult, TParam&gt;(
          TMock mockObject,
          Action&lt;TMock, TParam, EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt; action,
          TParam parameter,
          TResult result)
      where TMock : class
  {
      Moq.Mock.Get(mockObject)
          .Setup(m =&gt; action(m, parameter, Moq.It.IsAny&lt;EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt;()))
          .Callback&lt;TParam, EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt;((p, callback) =&gt;
                  callback(null, new DataPortalResult&lt;TResult&gt;(result, null, null)));
  }
  </code></pre>
  
  <p>I can call this method like so:</p>
  
  <pre><code>TestTools.ArrangeDataPortalResult&lt;IDataCache, DataObject, int&gt;(
      this.DataCacheMock,
      (mock, param, handler) =&gt; mock.GetDataObject(param, handler),
      dataObjectId,
      dataObject);
  </code></pre>
  
  <p>As it turns out, Moq doesn't like what I'm passing to the Setup method. It throws an exception, saying "Expression is not a method invocation".</p>
  
  <h2>Second Attempt</h2>
  
  <p>In this approach I do some manipulation of LINQ expressions (which I've never done before).</p>
  
  <pre><code>public static void ArrangeDataPortalResult&lt;TMock, TParam, TResult&gt;(
          TMock mockObject,
          Expression&lt;Action&lt;TMock&gt;&gt; methodCall, TResult result)
      where TMock : class
  {
      // Get the method that will be called on the mock object, and the method's parameters.
      var methodCallExpression = methodCall.Body as MethodCallExpression;
      var parameters = methodCallExpression.Arguments;
  
      // Create a new parameter list, and substitute Moq.It.IsAny&lt;EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt;() for the callback.
      // This is so that the test author doesn't need to write It.IsAny&lt;blah&gt;.
      var newParameters = parameters.Select(p =&gt; p).ToList();
      newParameters.RemoveAt(newParameters.Count - 1);
      var isAny = typeof(Moq.It).GetMethod("IsAny").MakeGenericMethod(typeof(EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;));
      var newCallbackParameterExpression = Expression.Call(null, isAny);
      newParameters.Add(newCallbackParameterExpression);
  
      // Create a new expression that contains the new IsAny parameter.
      var newMethodCallExpression = Expression.Call(methodCallExpression.Object, methodCallExpression.Method, newParameters);
  
      // Set up the mock object to expect a method call with the same parameters passed to it, but allow any callback to be passed to it.
      // Additionally, tell the mock object to immediately invoke its callback, and pass the given result to it.
      Moq.Mock.Get(mockObject)
          .Setup(Expression.Lambda&lt;Action&lt;TMock&gt;&gt;(newMethodCallExpression, methodCall.Parameters))
          .Callback&lt;TParam, EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt;((p, callback) =&gt; callback(null, new DataPortalResult&lt;TResult&gt;(result, null, null)));
  }
  </code></pre>
  
  <p>This method can be called like so.</p>
  
  <pre><code>TestTools.ArrangeDataPortalResult&lt;IDataCache, int, DataObject&gt;(
      this.DataCacheMock,
      mock =&gt; mock.GetDataObject(123, null),
      dataObject);
  </code></pre>
  
  <p>This works, and I might settle for something like this if necessary. Unfortunately if I were to accidentally call the wrong method of DataCacheMock (maybe it has an overload that takes a string instead of an int), then I would get a run time error rather than a compile time error.</p>
  
  <h2>Third Attempt</h2>
  
  <pre><code>public static void ArrangeDataPortalResultMoq&lt;TMock, TParam, TResult&gt;(
          Expression&lt;Action&gt; methodCall, TResult result)
      where TMock : class
  {
      // Get the method that will be called on the mock object, and the method's parameters.
      // (This part is the same.)
  
      // Create a new parameter list, and substitute Moq.It.IsAny&lt;EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt;() for the callback.
      // (This part is the same.)
  
      // Create a new expression that contains the new IsAny parameter.
      var newMethodCallExpression = Expression.Call(Expression.Parameter(typeof(TMock), "mock"), methodCallExpression.Method, newParameters);
  
      // Get the real mock object referred to in the method call.
      var mockObject = Expression.Lambda&lt;Func&lt;TMock&gt;&gt;(methodCallExpression.Object).Compile()();
  
      // Set up the mock object to expect a method call with the same parameters passed to it, but allow any callback to be passed to it.
      // Additionally, tell the mock object to immediately invoke its callback, and pass the given result to it.
      Moq.Mock.Get(mockObject)
          .Setup(Expression.Lambda&lt;Action&lt;TMock&gt;&gt;(newMethodCallExpression, Expression.Parameter(typeof(TMock), "mock")))
          .Callback&lt;TParam, EventHandler&lt;DataPortalResult&lt;TResult&gt;&gt;&gt;((p, callback) =&gt; callback(null, new DataPortalResult&lt;TResult&gt;(result, null, null)));
  }
  </code></pre>
  
  <p>This version gets the mock object from the expression you pass to it, so you don't have to mention the mock object twice when you call the helper method:</p>
  
  <pre><code>TestTools.ArrangeDataPortalResultMoq&lt;IDataCache, int, ceQryUomsBO&gt;(
      () =&gt; this.DataCacheMock.GetDataObject(dataObjectId, null),
      dataObject);
  </code></pre>
  
  <p>This approach still has the same problem with types though.</p>
  
  <p>I (and future test authors) could probably deal with the verbose syntax mentioned at the top, and we could probably deal with the lesser type safety since the test would just fail. I'd still like to see if this is possible with Moq though; I've gone this far down the rabbit hole. :-)</p>