Reputation: 89
Please see the code below. Using in-memory hosting of httpclient, and Passing httpclient object to controller in order to unit test action method. But I am getting "Internal Server Error" ReasonPhrase upon HttpResponseMessage response =_httpClient.GetAsync. Please help me, is it correct approach?
private readonly HttpClient _httpClient;
public SecurityMfMvcController(HttpClient httpClient)
{
this._httpClient = httpClient;
}
[HttpGet]
public ActionResult GetSecuritiesMfs()
{
try
{
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response =
_httpClient.GetAsync(ConfigurationManager.AppSettings["ApiUrl"] + "SecuritiesWebApiMf").Result;
response.EnsureSuccessStatusCode();
List<SecurityMutualFundDto> list =
response.Content.ReadAsAsync<List<SecurityMutualFundDto>>().Result;
return View("SecuritiesMf", list);
}
catch (Exception ex)
{
return View("Error", ex.Message);
}
}
//Unit Test Method for this Action
[Test]
public void TestActionGetSecuritiesMfs()
{
var config = new HttpConfiguration()
{
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always
};
//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
using (var client = new HttpClient(new InMemoryHttpContentSerializationHandler(server)))
{
System.Uri uri = new System.Uri("http://localhost:55893/api/");
client.BaseAddress = uri;
var controller = new SecurityMfMvcController(client);
var result = controller.GetSecuritiesMfs();
Assert.IsNotNull(result);
}
}
//MessageHandler
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;
}
}
Upvotes: 0
Views: 1783
Reputation: 89
I got it working, correct me in case of anything wrong here. I have to create a "FakeHttpMessageHandler" as below and the content type to match System.Net.Http.StreamContent for application/json content type. the below code is working to unit test mvc action method using httpclient to call WebAPI. however I need to double check whether this is the right approach for unit test, will review further.
[Test]
public void TestActionMethodSelectByIdUsingFakeHandler()
{
var uobj = new UnitTestForApiController();
var testobj= uobj.GetsecuritiesMfsList();
MemoryStream stream = new MemoryStream();
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, testobj);
var response = new HttpResponseMessage(HttpStatusCode.OK)
{Content = new StreamContent(stream)};
using (var httpClient = new HttpClient(new FakeHandler
{
Response = response,
InnerHandler = new HttpClientHandler()
}))
{
System.Uri uri = new System.Uri("http://localhost:55893/api/");
httpClient.BaseAddress = uri;
var controller = new SecuritiesMfMvcController(httpClient);
var result = controller.Select(2155) as ViewResult;
Assert.IsNotNull(result);
Assert.AreEqual(result.ViewName,"Select");
Assert.AreEqual(result.Model, testobj.FirstOrDefault());
}
//FakeHandler class goes as below
public class FakeHandler : DelegatingHandler
{
public HttpResponseMessage Response { get; set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() => Response, cancellationToken);
}
}
//We can also have logic for abstracting appropriate StreamContent Creation into FakeContent class like below:
public class FakeHttpContent : HttpContent
{
public object Content { get; set; }
public FakeHttpContent(object content)
{
Content = content;
}
protected async override Task SerializeToStreamAsync(Stream stream,
TransportContext context)
{
MemoryStream ms = new MemoryStream();
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, Content);
await ms.CopyToAsync(stream);
}
protected override bool TryComputeLength(out long length)
{
length = Content.ToString().Length;
return true;
}
}
Upvotes: 1
Reputation: 5255
You could mock your http request pipeline and test your action:
var mockHttpRequest = new Mock<HttpRequestMessage>(new object[] {new HttpMethod("GET"), "www.someuri.com"});
var mockHttpConfig = new Mock<HttpConfiguration>();
var mockRouteData = new Mock<IHttpRouteData>();
var mockHttpContext =
new Mock<HttpControllerContext>(new object[]
{mockHttpConfig.Object, mockRouteData.Object, mockHttpRequest.Object});
Then set your controller object with these values:
var controller = new YourController();
controller.ControllerContext = mockHttpContext.Object;
controller.Request = controller.ControllerContext.Request;
response = controller.SecuritiesMF();
and you could check your response as follows:
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Upvotes: 1