Home > Software design >  NullReferenceException when testing API implemented by Refit
NullReferenceException when testing API implemented by Refit

Time:09-26

I'm trying to test some error responses (BadRequest, Unauthorized, ...) with Refit and so I implemented a TestHandler that returns any desired response. The response works fine with an "OK" (HTTP status code 200) response:

public class Program
{
    private static async Task Main()
    {
        var api = RestService.For<ITest>(
            new HttpClient(new TestHandler(HttpStatusCode.OK))
                {
                    BaseAddress = new Uri("https://example.com")
                }
            );
        Console.WriteLine(await api.Test("foo").ConfigureAwait(false));
    }
}

public interface ITest
{
    [Get("/foo/{bar}")]
    Task<string> Test(string bar);
}

public class TestHandler : HttpMessageHandler
{
    private readonly HttpResponseMessage _response;

    public TestHandler(HttpStatusCode httpStatusCode)
        => _response = new HttpResponseMessage(httpStatusCode) { Content = new StringContent("Yay!") };

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        => Task.FromResult(_response);
}

However, whenever I change the response status code to, for example, BadRequest (400), NotFound (404) or Unauthorized (401) Refit throws a NullReferenceException:

Object reference not set to an instance of an object.
   at Refit.DefaultApiExceptionFactory.<CreateExceptionAsync>d__4.MoveNext() in /_/Refit/RefitSettings.cs:line 183
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Refit.RequestBuilderImplementation.<>c__DisplayClass14_0`2.<<BuildCancellableTaskFuncForMethod>b__0>d.MoveNext() in /_/Refit/RequestBuilderImplementation.cs:line 313
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at RefitTest.Program.<Main>d__0.MoveNext() in C:\Users\Rob\Source\Repos\RefitTest\RefitTest\Program.cs:line 18

This points to RefitSettings.cs line 183. But I can't figure out why a 200 OK would work but any other response won't? What am I doing wrong?

Edit: In an effort to debug this further I cloned Refit and swapped the Refit NuGet package for a project reference to the Refit project. This results in a InvalidOperationException: ITest doesn't look like a Refit interface. Make sure it has at least one method with a Refit HTTP method attribute and Refit is installed in the project. Also, going back a few versions (I've gone to 5.2.4) doesn't help.

CodePudding user response:

Found it, with help from a colleague! Turns out the HttpResponseMessage needs some/any RequestMessage.

Change

public TestHandler(HttpStatusCode httpStatusCode)
    => _response = new HttpResponseMessage(httpStatusCode)
    { 
        Content = new StringContent("Yay!") 
    };

To:

public TestHandler(HttpStatusCode httpStatusCode)
    => _response = new HttpResponseMessage(httpStatusCode)
    {
        RequestMessage = new(),  // <-- This one here...
        Content = new StringContent("Yay!")
    };

And it works as expected. As in the question (and comments), I was close but, apparently half-asleep, because it's exactly where it pointed me at.

  • Related