In my Web API app, I'm using HttpServer to contain my controller in unit tests, and I'm using HttpClient to call it directly, eg:
[Fact]
public void TestMyController()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
var server = new HttpServer(config);
var client = new HttpClient(server);
var response = client.GetAsync("http://localhost/api/test/values").Result;
}
I've noticed (by stepping through the debugger, and confirmed on other SO posts), that the JsonFormatter is not really running - it's initialized, but not exercised. Since this test isn't opening a socket, and the HttpClient is directly invoking the HttpServer through the HttpMessageHandler API, it does make sense that formatting/serialization isn't run because it's not needed.
In my case, I have some custom formatting/serialization/deserialization code that isn't being hit during these tests, but it's hit when I run in a real web server. I'd like to exercise that code in these tests; and it also just seems risky to exclude the serialization/deserialization code path when testing. Any advice on this?
Following is a quick example of what you could do to force formatters to go through serialization/deserialization. Here we are converting ObjectContent to StreamContent. In the below code, the call to CopyToAsync triggers a path where formatters are forced to serialize. In case of deserilization, in order to make sure we go through formatters we want the content to be of type other than ObjectContent as ReadAsAsync has internal logic which special cases ObjectContnent and we want to circumvent it.
HttpClient client = new HttpClient(new InMemoryHttpContentSerializationHandler(new HttpServer(config)));
public class InMemoryHttpContentSerializationHandler : DelegatingHandler
{
public InMemoryHttpContentSerializationHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content = await ConvertToStreamContentAsync(request.Content);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Content = await ConvertToStreamContentAsync(response.Content);
return response;
}
private async Task<StreamContent> ConvertToStreamContentAsync(HttpContent originalContent)
{
if (originalContent == null)
{
return null;
}
StreamContent streamContent = originalContent as StreamContent;
if (streamContent != null)
{
return streamContent;
}
MemoryStream ms = new MemoryStream();
await originalContent.CopyToAsync(ms);
// Reset the stream position back to 0 as in the previous CopyToAsync() call,
// a formatter for example, could have made the position to be at the end
ms.Position = 0;
streamContent = new StreamContent(ms);
// copy headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
{
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return streamContent;
}
}
Related
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
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;
A long time later again ...
In version 3.8.3 of the Firely SDK the FhirClient now has support for taking the HttpClient in its constructor, so this may make unit testing apis much easier. It wasn't clear from your post what you were testing here...
I wrote a blog post on using it for this type of testing
Roughly something like ...
[TestMethod]
public async Task SubscribeAndReturnSubscriptionIdAsync()
{
using (var fhirServerFactory = new UnitTestFhirServerApplication())
using (var httpclient = fhirServerFactory.CreateClient())
{
var server = new FhirClient("http://server.fire.ly", httpclient);
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 subscription = await server.CreateAsync(subscriptionResource);
// ...
}
}
Im creating a list of components in Blazor, each one of these components need to request some data from a webpage. The list are created as follows on a .razor page:
#foreach(stringcomp in Complist){
<myComponent />
}
around 100 components are created. On all of these components the following URL request is preformed (using this code):
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await GetUrl("some url here");
}
}
public async Task<string> GetUrl(string url)
{
HttpClient client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("User-Agent", "get data service");
var response = await client.SendAsync(request).ConfigureAwait(false);
string res = null;
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var streamReader = new StreamReader(responseStream);
res = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}
return res;
}
Doing this I'm running in to some problems where most of my calls to SendAsync never returns a value. I have come to understand that this is because of a lock-state but for the life of me can't figure out how to solve it. most similar answers suggest setting .ConfigureAwait(false) but this does not yeald a different result in my case.
So my question is: Hos can i request webbpages simultaneously in different components and be sure that they won't hang/lookup. As theres many requests that some times takes a long time (5-10 sec) to complete it's not an alternative to do them synchronously.
It might also be of importance to mention that me code and pages are separated, every .razor page are using #inherits to get its functions/logic
Try to use IHttpClientFactory as follows:
[Inject] public IHttpClientFactory clientFactory { get; set;}
using System.IO;
Your GetUrl method:
public async Task<string> GetUrl(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("User-Agent", "get data service");
var client = clientFactory.CreateClient();
var response = await client.SendAsync(request);
string res = null;
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
var streamReader = new StreamReader(responseStream);
res = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}
return res;
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// More code here...
}
Hope this works...
I have a number of calls I make to a webapi client which return a Task
something like this
public async Task<TResp> GetMyThingAsync(TReq req)
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(BaseURI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
await HttpRuntime.Cache.GetToken().ContinueWith((t) =>
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("XXX", t.Result);
});
var httpResponseMessage = await client.PostAsXmlAsync<TReq>("This/That/", req);
httpResponseMessage.EnsureSuccessStatusCode();
var resp = await httpResponseMessage.Content.ReadAsAsync<TResp>();
return resp;
}
}
the calls to the api can of course return 500's or some other problem. EnsureSuccessStatusCode() obviously throws if something like that happens, but then its too late to do anything with any information in the response.
is there a nice way of dealing with this?
I understand you can add a messageHandler with the client, something like
HttpClient client = HttpClientFactory(new ErrorMessageHandler()) ..
var customHandler = new ErrorMessageHandler()
{ InnerHandler = new HttpClientHandler()};
HttpClient client = new HttpClient(customHandler);
is this the way to go? what would the ErroMessageHandler look like and do to return something useful to the calling controller...?
thanks muchly
nat
Creating a custom handler can be an elegant solution to go about logging the exception or validating the response. I am not sure if the called controller is waiting for a meaningful response from the clients end if it encounters an exception. I think the real important part is to make sure you (the client) handle the web apis errors gracefully.
This can be done in a couple of ways:
You can handle exceptions locally inside the calling method. You can use the HttpResponseMessage.IsSuccessStatusCode property which indicated if a bad response returned instead of calling httpResponseMessage.EnsureSuccessStatusCode() which throws an exception, and return a custom ErrorResponse (or do whatever you decide):
var client = new HttpClient() // No need to dispose HttpClient
client.BaseAddress = new Uri(BaseURI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
var token = await HttpRuntime.Cache.GetToken();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("XXX", token);
var httpResponseMessage = await client.PostAsXmlAsync<TReq>("This/That/", req);
if (!httpResponseMessage.IsSuccessStatusCode)
{
return Task.FromResult(new ErrorResponse()) // Create some kind of error response to indicate failure
}
var resp = await httpResponseMessage.Content.ReadAsAsync<TResp>();
return resp;
Create a ErrorLoggingHandler which can log exceptions (or do something else) received from the web api:
public class ErrorLoggingHandler : DelegatingHandler
{
private readonly StreamWriter _writer; // As a sample, log to a StreamWriter
public ErrorLoggingHandler(Stream stream)
{
_writer = new StreamWriter(stream);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
// This would probably be replaced with real error
// handling logic (return some kind of special response etc..)
_writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri,
(int) response.StatusCode, response.Headers.Date);
}
return response;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_writer.Dispose();
}
base.Dispose(disposing);
}
}
Then, you can create your HttpClient using HttpClientFactory:
var httpclient = HttpClientFactory.Create(new ErrorLoggingHandler(new FileStream(#"Location", FileMode.OpenOrCreate)));
In my integration test the object schoolyearCreateRequest sent to /api/schoolyears url contains only null values when passing to the Post([FromBody] SchoolyearCreateRequest request) action parameter.
But when I use fiddler:
http://localhost:6320/api/schoolyears
Content-Type: application/json
Request Body:
{ SchoolyearDto:
{ Id: 10 }
}
Then it works and the SchoolyearDto is not null.
What is the problem in my integration test?
var schoolyearCreateRequest = new SchoolyearCreateRequest
{
SchoolyearDto = new SchoolyearDto(),
SchoolclassCodeDtos = new List<SchoolclassCodeDTO>(),
TimeTablesWeekAddedWeekA = new List<TimeTableDTO>(),
TimeTablesWeekAddedWeekAB = new List<TimeTableDTO>()
};
// Arrange
const string url = "api/schoolyears/";
var request = new HttpRequestMessage(HttpMethod.Post, _server.BaseAddress + url);
request.Content = new ObjectContent<SchoolyearCreateRequest>(schoolyearCreateRequest,new JsonMediaTypeFormatter());
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// Act
var response = _client.PostAsync(_server.BaseAddress + url, request, new JsonMediaTypeFormatter(), new CancellationToken()).Result;
// Assert
Assert.That(response.StatusCode == HttpStatusCode.Created);
UPDATE:
I made it working now in my integration test too:
replace these lines:
request.Content = new ObjectContent<SchoolyearCreateRequest>(schoolyearCreateRequest,new JsonMediaTypeFormatter());
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
with this line:
var postData = new StringContent(JsonConvert.SerializeObject(schoolyearCreateRequest), Encoding.UTF8, "application/json");
Why do I have to serialize the data by myself? And why is nearly nobody doing this approach with web api integration testing? All blogs I read showed the usage of the ObjectContent ??
You can take a look at my answer in the following post:
How do I exercise Formatters in tests using HttpServer?
Also, you can take a look at my blog post which was written long time back, but is still relevant:
http://blogs.msdn.com/b/kiranchalla/archive/2012/05/06/in-memory-client-amp-host-and-integration-testing-of-your-web-api-service.aspx
UPDATE:
Since there seems to be confusion around this, following is a complete example of an in-memory test. Its a bit crude but still should give you an idea.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using WebApplication251.Models;
namespace WebApplication251.Tests.Controllers
{
[TestClass]
public class PeopleControllerTest
{
string baseAddress = "http://dummyhost/";
[TestMethod]
public void PostTest()
{
HttpConfiguration config = new HttpConfiguration();
// use the configuration that the web application has defined
WebApiConfig.Register(config);
HttpServer server = new HttpServer(config);
//create a client with a handler which makes sure to exercise the formatters
HttpClient client = new HttpClient(new InMemoryHttpContentSerializationHandler(server));
Person p = new Person() { Name = "John" };
using (HttpResponseMessage response = client.PostAsJsonAsync<Person>(baseAddress + "api/people", p).Result)
{
Assert.IsNotNull(response.Content);
Assert.IsNotNull(response.Content.Headers.ContentType);
Assert.AreEqual<string>("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());
Person recPerson = response.Content.ReadAsAsync<Person>().Result;
Assert.AreEqual(p.Name, recPerson.Name);
}
}
}
public class InMemoryHttpContentSerializationHandler : DelegatingHandler
{
public InMemoryHttpContentSerializationHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content = await ConvertToStreamContentAsync(request.Content);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Content = await ConvertToStreamContentAsync(response.Content);
return response;
}
private async Task<StreamContent> ConvertToStreamContentAsync(HttpContent originalContent)
{
if (originalContent == null)
{
return null;
}
StreamContent streamContent = originalContent as StreamContent;
if (streamContent != null)
{
return streamContent;
}
MemoryStream ms = new MemoryStream();
await originalContent.CopyToAsync(ms);
// Reset the stream position back to 0 as in the previous CopyToAsync() call,
// a formatter for example, could have made the position to be at the end
ms.Position = 0;
streamContent = new StreamContent(ms);
// copy headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
{
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return streamContent;
}
}
}
We were developing a sample MWC application with the logic of Business ,DataAccess,Data Layers.
In core Project we used the following code for consuming data from json parsing. This code works fine for Xamarin.Android and Xamarin.iOS, but for windows phone it shows error as 'System.Net.WebRequest does not contain a definition for GetResponse and no extension method for GetResponse...'
We tried to use Async methods for consuming WCF Rest service json data, but it returned as null before the completed method called.
Is it possible to wait and get data from completed method to return the json collection? if no please suggest how to achieve the same.
public String login<T>(T item) where T : BusinessLayer.Contracts.IBusinessEntity, new()
{
var request = HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "get";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
string nss = content.ToString();
check = nss;
return nss;
}
}
return Check;
}
Edit: I have included the sample code of Async function.
Before Executing the DownloadStringCompleted event it returns null value. We need that DownloadStringCompleted output string for further process.
Note: We were following the logic of Tasky in Xamarin
async Task<string> AccessTheWebAsync(string url)
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (sender, e) =>
{
string data = (string)e.Result;
check = data;
};
webClient.DownloadStringAsync(new Uri(url));
return check;
}
public async Task<string> login<T>(T item) where T : BusinessLayer.Contracts.IBusinessEntity, new()
{
return check = await AccessTheWebAsync(item.url);
}
Your asynchronous code is not using Task correctly. It should use TaskCompletionSource to get the job done:
Task<string> AccessTheWebAsync(string url)
{
var source = new TaskCompletionSource<string>();
var webClient = new WebClient();
webClient.DownloadStringCompleted += (sender, e) =>
{
source.TrySetResult((string)e.Result);
};
webClient.DownloadStringAsync(new Uri(url));
return source.Task;
}
Before, your function was returning before the event fired. Using the task source wraps it in a task properly and fixes this problem.
You will also need to hook up the error event and call TrySetException to finish the implementation.