Search code examples
c#moqrestsharp

Null Data on mocked IRestClient.ExecuteAsync result


I have a method using RestSharp (110.2.0) that I'd like to unit test but am having difficulty.

Sample method:

private IRestClient _restClient;

public async Task<string> GetToken()
{
    var pw = await _vault.GetValue("pw");
    var un = await _vault.GetValue("un");
    
    var request = new RestRequest(new Uri("login"));
    request.AddJsonBody(new 
    {
        Username = un,
        Password = pw
    });
    
    var response = await _restClient.ExecutePostAsync<string>(request);
    return response.Data;
}

Sample test:

private AutoMocker _mocker;

[Fact]
public async Task SomeTest()
{
    _mocker.GetMock<IRestClient>()
        .Setup(m => m.ExecuteAsync(It.Is<RestRequest>(r => r.Resource == "login"), It.IsAny<CancellationToken>()))
        .ReturnsASync(new RestResponse
            {
                StatusCode = HttpStatusCode.OK, ResponseStatus = ResponseStatus.Completed,
IsSuccessStatusCode = true,
Content = "authToken",
            });
            
    var result = await _uut.GetToken();
    
    Assert.Equal("authToken", result);
}

For the sake of this scenario, ignore that I'm essentially testing that my mock returns what I want. Actual code is more comprehensive and the http stuff is just a small step in the process (but is the blocker right now).

Running that as is, I get a null reference exception. Using answers from this question I apply the following changes to the test:

[Fact]
public async Task SomeTest()
{
    var serializerConfig = new SerializerConfig();
    serializerConfig.UseDefaultSerializers();
    
    _mocker.GetMock<IRestClient>()
        .Setup(m => m.Serializers)
        .Returns(new RestSerializers(serializerConfig));
    
    ...rest of test code

This resolves the null reference exception, but response.Data is always null. I can see the expected value in content, but not in Data.

I've attempted to change the mock setup to return this instead:

.ReturnsAsync(new RestResponse<string>(new RestRequest())
{
    StatusCode = HttpStatusCode.OK, ResponseStatus = ResponseStatus.Completed,
    IsSuccessStatusCode = true,
    Content = "authToken",
    Data = "authToken"
});

But that changes nothing. I've also added ContentType = "application/json; charset=utf-8" to the mocked response, again with no success.

I am aware of the suggestion at RestSharp Docs, but it is currently impractical for me.


Solution

  • Extrapolating from the suggestion the RestSharp Docs, I modified my test to look like this:

    public class TestClass
    {
        public TestClass()
        {
            _mocker = new AutoMocker();
            _mockHttp = new MockHttpMessageHandler(); //from RichardSzalay.MockHttp
            _mocker.Use<IRestClient>(new RestClient(new RestClientOptions("http://localhost"){ ConfigureMessageHandler = _ => _mockHttp }));
            _uut = _mocker.CreateInstance<ClassToTest>();
        }
        
        [Fact]
        public async Task SomeTest()
        {
            _mockHttp
                .When("*/REST/Login")
                .Respond("application/json", JsonConvert.SerializeObject("authToken"));
                
            var result = await _uut.GetToken();
        
            Assert.Equal("authToken", result);
        }
    }
    

    This works and is actually a much cleaner implementation than I expected. Initially I was worried that I wouldn't be able to essentially modify the MockHttpMessageHandler after assigning it to the IRestClient, but that is not the case.