Mike Taverne
Mike Taverne

Reputation: 9352

How to mock RestSharp AddDefaultHeader method

I'm trying to unit test this method which uses RestSharp (version 106.6.9):

public UserInfo GetUserInfo(string oauthToken)
{
    _restClient.AddDefaultHeader("Authorization", "Bearer " + oauthToken);

    var request = new RestRequest(_userInfoUrl);
    var response = _restClient.Execute<UserInfo>(request);
    if (!response.IsSuccessful)
    {
        throw new ApplicationException("Request for user info failed with HTTP status code " + response.StatusCode);
    }

    return response.Data;
}

This is what I tried:

[Test]
public void ShouldFailIfOAuthTokenExpired()
{
    var mockRestClient = new Mock<IRestClient>();
    mockRestClient.Setup(m => m.AddDefaultHeader("Authorization", "Bearer Some expired token"));
    mockRestClient.Setup(m => m.Execute(It.IsAny<IRestRequest>())).Returns(new RestResponse() { StatusCode = System.Net.HttpStatusCode.Unauthorized });

    var svc = new MyService(mockRestClient.Object, "my endpoint");

    Assert.Throws<ApplicationException>(() => svc.GetUserInfo("Some expired token"));
}

This code builds, but throws a runtime exception that prevents my test from working:

System.NotSupportedException : Unsupported expression: m => m.AddDefaultHeader("Authorization", "Bearer Some expired token")
Extension methods (here: RestClientExtensions.AddDefaultHeader) may not be used in setup / verification expressions.

It appears there is no way for me to test my method if it includes a call to .AddDefaultHeader. I considered creating my own mock object, but the IRestClient interface requires dozens of methods.

Any suggestions on how to test my method?

Upvotes: 3

Views: 3387

Answers (2)

Aage
Aage

Reputation: 6252

If you want to check whether a default header was added you could use a spy like so:

    [Fact]
    public void Test()
    {
        var mock = new Mock<IRestClient>();
        var spy = new List<Parameter>();
        mock
            .Setup(m => m.DefaultParameters.Add(It.IsAny<Parameter>()))
            .Callback((Parameter p) => spy.Add(p));
        var instance = mock.Object;

        instance.AddDefaultParameter(new Parameter("Foo", "Bar", ParameterType.Cookie));

        Assert.Equal("Bar", spy.Single().Value);
    }

This uses the .Callback functionality of Moq which adds the added Parameter to the spy.

This works because you can see in the source code for RestSharp that this extension method calls AddDefaultParameter which in turn adds a Parameter to the DefaultParameters:

restClient.DefaultParameters.Add(p);

This example uses xUnit as a test runner, but the rest should be the same.

Upvotes: 3

Nkosi
Nkosi

Reputation: 246998

Moq does not mock extension methods. Figure out what the extension method accesses and that is what you mock.

The source for that extension method is available and ultimately calls

/// <summary>
/// Add a parameter to use on every request made with this client instance
/// </summary>
/// <param name="restClient">The IRestClient instance</param>
/// <param name="p">Parameter to add</param>
/// <returns></returns>
public static IRestClient AddDefaultParameter(this IRestClient restClient, Parameter p)
{
    if (p.Type == ParameterType.RequestBody)
    {
        throw new NotSupportedException(
            "Cannot set request body from default headers. Use Request.AddBody() instead.");
    }

    restClient.DefaultParameters.Add(p);

    return restClient;
}

Source

So depending on what you need to access, then restClient.DefaultParameters is what needs to be mocked.

If no actual functionality is actually needed and this is just to avoid null reference exception then set up the mock to stub all available properties using SetupAllProperties

//...

mockRestClient.SetupAllProperties();

//...

Upvotes: 2

Related Questions