Thursday, 10 January 2013

How to Mock RestSharp.ExecuteAsync

It took me a while to figure out how to Mock RestSharp’s ExecuteAsync method for unit testing purposes. Once you realise that ExecuteAsync takes a callback which is called when the REST call completes it all becomes reasonably straightforward, using Mocks Callback function;
Mock<IRestClient> restClient = new Mock<IRestClient>();
            restClient.Setup(c => c.ExecuteAsync<MyResult>(
                    Moq.It.IsAny<IRestRequest>(), 
                    Moq.It.IsAny<Action<IRestResponse<MyResult>, RestRequestAsyncHandle>>()))
                .Callback<IRestRequest, Action<IRestResponse<MyResult>, RestRequestAsyncHandle>>((request, callback) =>
                {
                    var responseMock = new Mock<IRestResponse<MyResult>>();
                    responseMock.Setup(r => r.Data).Returns(new MyResult() { Foo = "Bar" });
                    callback(responseMock.Object, null);
                });

In my scenario I wanted to use the built-deserialization and return a “MyResult".
This means that ExecuteAsync takes two parameters:
  • An IRestRequest
  • A callback, which is an Action<IRestResponse<AuthorisationResponse>, RestRequestAsyncHandle>

    That is just the callback you pass in and it is a delegate that takes two parameters, namely the IRestResponse and a RestRequestAsyncHandle.
When you are running this for real, RestSharp will execute your IRestRequest and then call your callback with the result. So in our Unit test, we can use Mock’s Callback feature to call your callback straight away. To your code that will just look like the website responded really quickly.

When I actually call the callback function, I also use Mock to mock the IRestResponse and just configure that mock to return a concrete instance of MyResult.

EDIT 9 November 2013

A fuller example:

public class Sud
{
    private IRestClient client;

    public Sud(IRestClient client)
    {
        this.client = client;
    }

    public MyResult Foo()
    {
        // NOTE: This code is obviously stupid; it just blocks the thread until the async task completes, which is rather pointless.
        // It's just an example :)
        MyResult result = null;
        var request = new RestRequest();
        var blocker = new AutoResetEvent(false);

        this.client.ExecuteAsync<MyResult>(
            request, 
            response =>
            {
                result = response.Data;
                blocker.Set();
            });
        blocker.WaitOne();
        return result;
    }
}

public class MyResult
{
    public string Name { get; set; }
}

[TestFixture]
public class Tests
{
    [Test]
    public void TestFoo()
    {
        Mock<IRestClient> restClient = new Mock<IRestClient>();
        restClient.Setup(c => c.ExecuteAsync<MyResult>(
                Moq.It.IsAny<IRestRequest>(),
                Moq.It.IsAny<Action<IRestResponse<MyResult>, RestRequestAsyncHandle>>()))
            .Callback<IRestRequest, Action<IRestResponse<MyResult>, RestRequestAsyncHandle>>((request, callback) =>
            {
                var responseMock = new Mock<IRestResponse<MyResult>>();
                responseMock.Setup(r => r.Data).Returns(new MyResult() { Name = "Billy Bob" });
                callback(responseMock.Object, null);
            });

        var Subject = new Sud(restClient.Object);

        var result = Subject.Foo();

        Assert.IsNotNull(result);
        Assert.AreEqual("Billy Bob", result.Name);
    }
}

5 comments:

  1. Dear Frans,
    thank you for this post. Very oftern it is useful to test our code before we have the service implemented on a website.

    However could you please better show a test unit code that uses the mock code you described, supposing that my callbackfunction is, for example, private void ChangePasswordCallback(IRestResponse response, RestRequestAsyncHandle arg2)?


    Thank you
    Enzo Contini

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hi Frans,

    I tried mocking IRestClient ExecuteAsync as per your example. Whenever i run the test runner, it fails at the step where i am setting up the mock for ExecuteAsync with following error:
    "System.NotSupportedException: Expression references a method that does not belong to the mocked object".
    On some research, i found that you cannot mock extension methods and ExecuteAsync is an extension method. Did you face this issue? If yes, how did you resolve it?

    ReplyDelete
    Replies
    1. I suspect I know what it is. There is an extension method with the signature

      public virtual RestRequestAsyncHandle ExecuteAsync<T>(IRestRequest request, Action<IRestResponse<T>, RestRequestAsyncHandle> callback)

      That is probably the one you are actually calling in your code (it's the one I am using in the example I have added above).

      However, you can't mock extensions methods. That extension internally calls the method on the class with this signature:

      public virtual RestRequestAsyncHandle ExecuteAsync(IRestRequest request, Action<IRestResponse, RestRequestAsyncHandle> callback)

      The Moq in my example mocks that underlying method, while the tested code calls the extension method. It is a bit disconcerting, but it works :)

      In other words, I suspect you may have tried to mock the extension method (as that is what your code is probably calling) rather than the underlying method.

      Delete
  4. Here is a gist of what I have done to mock ExecuteAsync without a type.

    https://gist.github.com/mrstebo/3636d3f86a4fe8e27205f2c4a0065f27

    ReplyDelete