Search code examples
hl7-fhir

How to mock response in FHIR Http Client calls to CreateAsync


When implementing calls to CreateAsync in the Hl7.Fhir.Rest.FhirClient library I'm struggling with how to mock a valid response. I know how to mock the dotnet-httpclient using a Mock HttpMessageHandler object and noticed there is a message handler argument that can be specified when creating the FhirClient. What I have tried to do is specify a message handler to the creation step that is a mock message handler object.

This simplified unit test attempts to mock the HttpMessageHandler and cause it to return a valid body and result code from the FhirClient's CreateAsync method call.

        [Fact]
        public async Task SubscribeAndReturnSubscriptionIdAsync()
        {
            var mockHttpMessageHandler = MockFhirHttpClientMessageHandler.MockSubscribeMessageResponse(new StringContent("{'id':'abc123','status':'active'}"), HttpStatusCode.Created);
            var subscriptionResource = new Subscription()
            {
                Criteria = "https://server.fire.ly/CareTeam",
                Status = Subscription.SubscriptionStatus.Active,
                Reason = "test",
                Channel = new Subscription.ChannelComponent()
                {
                    Type = Subscription.SubscriptionChannelType.RestHook,
                    Endpoint = "http://localhost:9999/AscomFhirApi/UpdateCareTeam",
                    Payload = "application/fhir+json"
                },
            };
            var serverUri = new Uri("http://server.fire.ly");
            var clientSettings = new FhirClientSettings()
            {
                PreferredFormat = ResourceFormat.Json
            };

            var fhirHttpClient = new Hl7.Fhir.Rest.FhirClient(serverUri, clientSettings, mockHttpMessageHandler.Object);

            var subscription = await fhirHttpClient.CreateAsync<Subscription>(subscriptionResource);

            Assert.NotEmpty(subscription.Id);
        }

The MockSubscribeMessageResponse method shown below creates the HttpMessageHandler that is passed to the FhirClient in the above test.

public static Mock<HttpMessageHandler> MockSubscribeMessageResponse(
            HttpContent content,
            HttpStatusCode code = HttpStatusCode.OK)
        {
            var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
            mockHttpMessageHandler.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
                    ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(new HttpResponseMessage
                {
                    StatusCode = code,
                    Content = content
                });
            return mockHttpMessageHandler;
        }

The error I'm getting is a Null Reference Exception in what looks like the HttpResponseMessage or response body.

System.NullReferenceException
Object reference not set to an instance of an object.
   at Hl7.Fhir.Rest.HttpToEntryExtensions.ToEntryResponse(HttpResponseMessage response, Byte[] body)
   at Hl7.Fhir.Rest.HttpClientRequester.ExecuteAsync(EntryRequest interaction)
   at Hl7.Fhir.Rest.BaseFhirClient.executeAsync[TResource](Bundle tx, IEnumerable`1 expect)
   at Tests.Unit.Core.Services.FirelyHttpClientShould.SubscribeAndReturnSubscriptionIdAsync() in C:\src\AscomIASharedAssignFHIRApi5\Tests.Unit.Core\Services\FirelyHttpClientShould.cs:line 60

Solution

  • You have probably figured this out long time ago, but the source of error is most probably missing RequestMessage, implementation of ToEntryResponse depends on response.RequestMessage.RequestUri being set. So I guess that what you need to do is:

    var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
    mockHttpMessageHandler.Protected()
        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
        .ReturnsAsync(new HttpResponseMessage
        {
            StatusCode = code,
            RequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost"),
            Content = content
        });
        return mockHttpMessageHandler;