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?
Related
I am trying to mock an Http Client that uses the IdentityModel extension to request a client credentials token.
var tokenResponse = await _httpClient.RequestClientCredentialsTokenAsync(requestContent);
I started doing the setup with:
var httpClient = new Mock<HttpClient>();
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = JsonContent.Create(new
{
access_token = "token",
expires_in = 5000
})
};
var tokenResponse = ProtocolResponse.FromHttpResponseAsync<TokenResponse>(httpResponseMessage);
httpClient.Setup(x => x.RequestClientCredentialsTokenAsync(It.IsAny<ClientCredentialsTokenRequest>(), It.IsAny<CancellationToken>())).Returns(tokenResponse);
But i end up with:
System.NotSupportedException : Unsupported expression: x => x.RequestClientCredentialsTokenAsync(It.IsAny(), It.IsAny())
Extension methods (here: HttpClientTokenRequestExtensions.RequestClientCredentialsTokenAsync) may not be used in setup / verification expressions.
How can i mock the RequestClientCredentialsTokenAsync extension?
Looking at the internals of RequestClientCredentialsTokenAsync we can see that the base request it is using is SendAsync, so we need to mock SendAsync.
Extension call:
response = await client.SendAsync(request, cancellationToken).ConfigureAwait();
Final Setup:
var httpClient = new Mock<HttpClient>();
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = JsonContent.Create(new
{
access_token = "token",
expires_in = 5000
})
};
httpClient.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(httpResponseMessage));
Result:
I have the following method to create a Http client with a mocked response:
public static HttpClient GetMockedHttpClient(string responseContent, HttpStatusCode httpStatusCode)
{
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = httpStatusCode,
Content = new StringContent(responseContent),
})
.Verifiable();
return new HttpClient(handlerMock.Object);
}
How can I make the response dependent on a specific url, so that the setup only takes effect when the HttpClient is called with a given address?
What you need to change is the line:
ItExpr.IsAny<HttpRequestMessage>(),
IsAny<HttpRequestMessage>() means the mock will catch any property of that type, if you want it to catch a specific uri you use Is<HttpRequestMessage>()
ItExpr.Is<HttpRequestMessage>(m => m.RequestUri == [your uri goes here]),
I would suggest using https://github.com/justeat/httpclient-interception.
I had to run similar tests and it did the job, although I used it to mock whole client.
Hello I dont get the difference between the following two asnyc functions, could someone explain it to me? Both of them doesnt return IRestResponse, so I cant access StatusCode field. Do I have to cast here?
public async Task<IRestResponse> Post<T>(string Ressource, T ObjTOPost) where T : new()
{
return await Task.Factory.StartNew(() =>
{
var client = new RestClient("test.com");
var request = new RestRequest(Ressource, Method.POST);
var response = client.Execute(request);
return response;
});
}
And this:
public async Task<IRestResponse> Post<T>(string Ressource, T ObjTOPost) where T : new()
{
var client = new RestClient("test.com");
var request = new RestRequest(Ressource, Method.POST);
var response = await client.ExecuteTaskAsync<T>(request);
return response;
}
Both of them doesnt return IRestResponse, so I cant access StatusCode field.
They return a Task<IRestResponse>. You can get the interface by awaiting the task, e.g.
var task = Post(resource, objectToPost);
IRestResponse response = await task;
Or in one line (more common):
var response = await Post(resource, objectToPost);
Difference between these two async functions
The second example is far more straightforward. The first example spins up an additional task and passes its awaitable back to the caller, whereas the second example awaits the RestClient directly. I see no reason to use the structure in the first example.
How can I mock HttpRequestMessage, specifically the CreateResponse?
var requestMessage = Substitute.For<HttpRequestMessage>();
requestMessage.CreateResponse().ReturnsForAnyArgs(
new HttpResponseMessage(HttpStatusCode.OK));
but I get the exception ...
NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException:
'Could not find a call to return from.
I've seen the questions ... How to mock the CreateResponse<T> extension method on HttpRequestMessage
And associated ... ASP.NET WebApi unit testing with Request.CreateResponse ...
But they don't seem to actually end up mocking the CreateResponse
Additional comments:
I'm trying to write a unit test around the starter of an Azure precompiled C# function ...
[FunctionName("Version")]
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequestMessage req,
TraceWriter log)
{
log.Info("Version function processed a request ... ");
return req.CreateResponse(HttpStatusCode.OK, "Version 0.0.1");
}
and the actual test, where I want to mock up the HttpRequestMessage, specifically the CreateReponse where I get the error is ...
[TestMethod]
public void Version_returns_value()
{
var requestMessage = Substitute.For<HttpRequestMessage>();
requestMessage.CreateResponse(Arg.Any<HttpStatusCode>(), Arg.Any<string>())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
var log = new CustomTraceWriter(TraceLevel.Verbose);
var httpResponseMessage = VersionFunction.Run(requestMessage, log);
var httpContent = httpResponseMessage.Content;
httpContent.Should().Be("Version 0.0.1 :: valid");
}
No need to mock anything here. Everything can be stubbed safely for this test. CreateResponse is an extension method that, internally, makes use of the request's associated HttpConfiguration. That is the only requirements that needs to be setup before using it in your test.
With that, if you update your test as follows, you should be able to properly exercise your test.
[TestMethod]
public async Task Version_returns_value() {
var expected = "\"Version 0.0.1\"";
var config = new HttpConfiguration();
var requestMessage = new HttpRequestMessage();
requestMessage.SetConfiguration(config);
var log = new CustomTraceWriter(TraceLevel.Verbose);
var httpResponseMessage = VersionFunction.Run(requestMessage, null);
var httpContent = httpResponseMessage.Content;
var content = await httpContent.ReadAsStringAsync();
content.Should().Be(expected);
}
I'm completely new to using async calls and await. I have the below unit test function:
public async static void POSTDataHttpContent(string jsonString, string webAddress)
{
HttpClient client = new HttpClient();
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
Console.WriteLine("response is: " + response);
}
The test completes without error, but I never see the Console.WriteLine print statement show up in output - I'm not sure why. I've been looking around and it sounds like I may need to set this up as a task? Could someone point me in the proper direction?
Since you are already awaiting an HttpResponseMessage, a simple (and consistent) solution is to return Task<HttpResponseMessage>.
var x = await POSTDataHttpContent("test", "http://api/");
public async Task<HttpResponseMessage> POSTDataHttpContent(
string jsonString, string webAddress)
{
using (HttpClient client = new HttpClient())
{
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
Console.WriteLine("response is: " + response);
return response;
}
}
That said, you also need to ensure that your test setup is correct. You cannot properly call an async method from a synchronous test. Instead, mark your test async as well and await the method you are calling. Furthermore, your test method must be marked as async Task as well since neither MS Test Runner nor other tools (NCrunch, NUnit) will properly deal with an async void test method:
[TestMethod]
public async Task TestAsyncHttpCall()
{
var x = await POSTDataHttpContent("test", "http://api/");
Assert.IsTrue(x.IsSuccessStatusCode);
}
I think the best thing to do here for you would be to opt for a Task return type instead of a void.
public async Task POSTDataHttpContent(string jsonString, string webAddress)
{
using (HttpClient client = new HttpClient())
{
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
// Assert your response may be?
}
}
And if you are really adamant about not using Tasks (which is not a good idea):
public void POSTDataHttpContent(string jsonString, string webAddress)
{
var Task = Task<HttpResponseMessage>.Run(async () => {
using (HttpClient client = new HttpClient())
{
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
return response;
}
});
Task.Wait();
Assert.IsNotNull(Task.Result);
}