0

I'm trying to verify the HttpContent of an HttpRequestMessage, but reading the content requires an async operation. How can I do this using Moq?

Illustrative example:

[TestMethod]
public async Task Example()
{
    var mockHttpMessageHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
    mockHttpMessageHandler
        .Protected()
        .Setup<Task<HttpResponseMessage>>(
            "SendAsync",
            ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()
        )
        .ReturnsAsync(new HttpResponseMessage()
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(""),
        })
        .Verifiable();

    // call some business component that should send the expected JSON via HTTP Post
    await ExecuteSomeBusinessComponentThatPostsViaHttp(httpClient);   

    this.MockHttpMessageHandler.Protected().Verify(
        "SendAsync",
        Times.Once(),
        ItExpr.Is<HttpRequestMessage>(request =>
            JToken.DeepEquals(
                // the next line does not compile
                JToken.Parse(await request.Content.ReadAsStringAsync()),
                JObject.FromObject(new { Result = new { Foo = "Bar" } }))),
        ItExpr.IsAny<CancellationToken>());
}

How can I get this test to compile? I.e., how can I use await within an It.Is(...) expression?

Alternatively, is there some other way to test the http request content?

joniba
  • 3,339
  • 4
  • 35
  • 49
  • 2
    What are you trying to test? Do you doubt that `StringContent` works? A mock handler like this is only useful to ensure that `HttpClient` and `StringContent` work. In fact, given it's a `StringContent`, there's no reason to parse the payload. Just compare it with the original `json` variable. If either HttpClient or StringContent fail this simple check, there's nothing you can do to fix them. – Panagiotis Kanavos Oct 13 '22 at 15:48
  • A mock would make sense if you wanted to return a fake response, or ensure that some *other* service made the correct call to HttpClient. It looks [that past me](https://stackoverflow.com/a/59732213/134204) had answered a similar question and found [Moq.Contrib.HttpClient](https://github.com/maxkagamine/Moq.Contrib.HttpClient) that can be used to simplify mocking HttpClient – Panagiotis Kanavos Oct 13 '22 at 15:53
  • @PanagiotisKanavos the example is contrived to clarify my question. It does not reflect the actual test being run, in which I am testing that a business component is posting the expected data via HTTP. – joniba Oct 13 '22 at 17:01
  • @PanagiotisKanavos How would you use Moq.Contrib.HttpClient for the case in the question? In the links you posted I don't see anything testing the request content. – joniba Oct 13 '22 at 17:04
  • 1
    Perhaps you can edit your example so it's more realistic? From here it looks like you're testing `HttpClient` and `StringContent`, as @PanagiotisKanavos notes. I don't think it would be that challenging to make a small class that injects `IHttpClientFactory` or `IHttpClient` and uses Moq to mock those. I'm just thinking that the techniques used (via the library or base Moq) to mock those types to capture the request would be different than those used for the scenario presented here currently. – Heretic Monkey Oct 13 '22 at 17:14
  • 1
    @joniba what are you trying to test in the first place? The code you wrote does nothing more than a `json==json`. The `request.Content` object is the same `StringContent` object you posted. Its content is the same `json` string you used. This mock handler doesn't make sense *in this particular test*. Unless you wanted to check the internals of HttpClient itself – Panagiotis Kanavos Oct 13 '22 at 18:11
  • @PanagiotisKanavos I edited the question. I hope it makes more sense now. – joniba Oct 16 '22 at 13:52
  • @HereticMonkey unfortunately IHttpClientFactory is not available in the .NET version I am using and updating is currently not an option. Also I would like to test the code as is, without having to refactor it. – joniba Oct 16 '22 at 13:59

0 Answers0