Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: In the handler.VerifyRequest ... #1

Closed
NelsonLamprecht opened this issue Mar 31, 2020 · 1 comment
Closed

Question: In the handler.VerifyRequest ... #1

NelsonLamprecht opened this issue Mar 31, 2020 · 1 comment

Comments

@NelsonLamprecht
Copy link

Question: In the handler.VerifyRequest portion of a test ...is there a way to log out what the url and response was that was monitored? Like, expected http://uri, received, http://uri2?

@maxkagamine
Copy link
Owner

Hi Nelson, apologies for the delay. You might have already found a solution, but there are a few ways you could go about this. Depending on the situation, if you know the expected url beforehand, it's generally best to restrict the Setup to only match that url:

handler.SetupRequest("https://example.com/expected")
    .ReturnsResponse(HttpStatusCode.OK);

await client.GetAsync("https://example.com/actual"); // Throws

In this case, GetAsync will throw either InvalidOperationException in Loose mode (as the mocked handler returned an implicit null which caused HttpClient to throw) or MockException in Strict mode (as the code called a method that wasn't mocked). The latter will show the actual url in the error message:

Message: 
  Moq.MockException : HttpMessageHandler.SendAsync(Method: GET, RequestUri: 'https://example.com/actual', Version: 1.1, Content: <null>, Headers:
  {
  }, CancellationToken) invocation failed with mock behavior Strict.
  All invocations on the mock must have a corresponding setup.

On the other hand, if you can't limit the Setup and all you need are the actual requested url(s), you can inspect Moq's Invocations list after the fact to see the calls made to the handler:

var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();

handler.SetupAnyRequest()
    .ReturnsResponse(HttpStatusCode.OK);

await client.GetAsync("https://example.com/foo");
await client.GetAsync("https://example.com/bar");

foreach (IInvocation invocation in handler.Invocations)
{
    var request = (HttpRequestMessage)invocation.Arguments[0];
    output.WriteLine($"{request.Method} {request.RequestUri}");
}

// Output:
// GET https://example.com/foo
// GET https://example.com/bar

(output here is xUnit's ITestOutputHelper)

Alternatively, you can put a Callback on the setup like so to log the urls as they're requested:

var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();

handler.SetupAnyRequest()
    .ReturnsResponse(HttpStatusCode.OK)
    .Callback((HttpRequestMessage request, CancellationToken _) =>
    {
        output.WriteLine($"{request.Method} {request.RequestUri}");
    });

await client.GetAsync("https://example.com/foo");
await client.GetAsync("https://example.com/bar");

Note that in both cases, the called method is always HttpMessageHandler.SendAsync, hence those arguments.

This won't get you the returned response, however. As far as I'm aware, there's currently no way to get a mocked method's return value, although there's an open PR to add it to IInvocation: devlooped/moq#921.

For the time being, if you need to capture both requests and responses, you may need to do something like this, similar to what one would do to create a response based on the request:

var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();

var responses = new List<HttpResponseMessage>();

handler.SetupAnyRequest()
    .Returns((HttpRequestMessage request, CancellationToken _) =>
    {
        var response = new HttpResponseMessage()
        {
            Content = new StringContent("Your response here"),
            RequestMessage = request
        };

        responses.Add(response);
        return Task.FromResult(response);
    });

await client.GetAsync("https://example.com/foo");
await client.GetAsync("https://example.com/bar");

foreach (HttpResponseMessage response in responses)
{
    var request = response.RequestMessage;
    output.WriteLine($"{request.Method} {request.RequestUri} {response.StatusCode} {await response.Content.ReadAsStringAsync()}");
}

// Output:
// GET https://example.com/foo OK Your response here
// GET https://example.com/bar OK Your response here

In theory, the response helpers' configure action could be used here for some added convenience, but they don't currently set RequestMessage on the response objects. I can see about adding that, though.

I realize I'm a bit late in getting back to you (sorry again), but if you have any more questions, or if I misunderstood, let me know and I'll do my best to help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants