Unit Testing / Integration Testing Web API with HttpClient in Visual Studio 2013
Referencing the following article I was able to do...
ASP.NET Web API integration testing with in-memory hosting
by working with a HttpServer
and a HttpConfiguration
passed into HttpClient
. In the following example I created a simple ApiController
that uses attribute routing. I configured the HttpConfiguration
to map attribute routes and then passed it to the new HttpServer
. The HttpClient
can then use the configured server to make integration test calls to the test server.
public partial class MiscUnitTests {
[TestClass]
public class HttpClientIntegrationTests : MiscUnitTests {
[TestMethod]
public async Task HttpClient_Should_Get_OKStatus_From_Products_Using_InMemory_Hosting() {
var config = new HttpConfiguration();
//configure web api
config.MapHttpAttributeRoutes();
using (var server = new HttpServer(config)) {
var client = new HttpClient(server);
string url = "http://localhost/api/product/hello/";
var request = new HttpRequestMessage {
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var response = await client.SendAsync(request)) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
}
}
public class ProductController : ApiController {
[HttpGet]
[Route("api/product/hello/")]
public IHttpActionResult Hello() {
return Ok();
}
}
}
There was no need to have another instance of VS running in order to integration test the controller.
The following simplified version of the test worked as well
var config = new HttpConfiguration();
//configure web api
config.MapHttpAttributeRoutes();
using (var server = new HttpServer(config)) {
var client = new HttpClient(server);
string url = "http://localhost/api/product/hello/";
using (var response = await client.GetAsync(url)) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
In your case you need to make sure that you are configuring the server properly to match your web api setup. This would mean that you have to register your api routes with the HttpConfiguration
object.
var config = new HttpConfiguration();
//configure web api
WebApiConfig.Register(config);
//...other code removed for brevity
Related videos on Youtube
Comments
-
duyn9uyen over 3 years
I am having a hard time trying to test my API controller with Visual Studio 2013. My one solution has a Web API Project and a Test project. In my test project, I have a Unit Test with this:
[TestMethod] public void GetProduct() { HttpConfiguration config = new HttpConfiguration(); HttpServer _server = new HttpServer(config); var client = new HttpClient(_server); var request = new HttpRequestMessage { RequestUri = new Uri("http://localhost:50892/api/product/hello"), Method = HttpMethod.Get }; request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using (var response = client.SendAsync(request).Result) { Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var test = response.Content.ReadAsAsync<CollectionListDTO>().Result; } }
I keep getting a 404. I have tried running my API with one instance of Visual Studio (IIS Express), and trying to debug this Unit Test in another instance. But no luck. I have verified that I can put this URL in a browser (when one Visual Studio is debugging) and I see my JSON response. But I can't figure out how to get it to work with my unit test and
HttpClient
. I have tried to find examples online but can't seem to find one. Can someone help?UPDATE 1: I tried adding in a route but nothing happened.
HttpConfiguration config = new HttpConfiguration(); // Added this line config.Routes.MapHttpRoute(name: "Default", routeTemplate: "api/product/hello/"); HttpServer _server = new HttpServer(config); var client = new HttpClient(_server); [...rest of code is the same]
Here is my API Controller
[HttpGet] [Route("api/product/hello/")] public IHttpActionResult Hello() { return Ok(); }
UPDATE Resolution: I was able to get it to work if I new up
HttpClient
without aHttpServer
object. I would still need to have two instances of VS running though. 1 running my API code and another to run the Unit Test.Here is a working method.
[TestMethod] public void Works() { var client = new HttpClient(); // no HttpServer var request = new HttpRequestMessage { RequestUri = new Uri("http://localhost:50892/api/product/hello"), Method = HttpMethod.Get }; request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using (var response = client.SendAsync(request).Result) { Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); } }
Anyone know why it doesn't work with a
HttpServer
and aHttpConfiguration
passed intoHttpClient
? I have seen many examples that use this.-
Alexandru Marculescu almost 8 yearsmight not make a difference, but why not use GetAsync directly?
-
Nkosi almost 8 yearsAre you using OWIN pipeline with start up? You may need to use
TestServer
. blogs.msdn.microsoft.com/webdev/2013/11/26/… -
duyn9uyen almost 8 yearsI am not using owin.
-
Nkosi almost 8 yearswhat version of asp.net-mvc web api are you using?
-
Nkosi almost 8 yearsDuring unit test there is not host for the
HttpServer
. which is why when you try to access the URL you get a 404. -
duyn9uyen almost 8 years@Nkos. I'm not sure I understand. What do I need to do so that I can make this http request?
-
Fran almost 8 yearsWhere is the code for the method you are trying to test? This looks like an integration test because you aren't mocking/faking out any dependencies.
-
Nkosi almost 8 yearsTake a look at this article, doing what you are trying to do. I noticed that you are not configuring your
HttpConfiguration
to handle routes/requests. -
duyn9uyen almost 8 yearsAdding in a route to the config didn't seem to work. And yes @Fran, this is more an integration test. The end point works because I can put the url directly into a browser and I get a response. I just cant seem to get a successful HTTP request within my unit test.
-
Nkosi almost 8 years@duyn9uyen update you post with what you tried. If you added the route like in the article and used the same url you had in your original post it's not going to work because it not going to match the routes
-
duyn9uyen almost 8 years@Nkosi, I have added some updates. Thanks for checking.
-
Nkosi almost 8 years@duyn9uyen Take a look at my answer. I was eventually able to get back to this question and provide you with an example. It's based on the information at the article I posted for you in a previous comment.
-
Nkosi almost 8 years@duyn9uyen check updated answer and comments. got your code to work.
-
Blue Clouds about 6 yearsWhy accessing data is not mentioned in the solution. We do not call an api just to check if the status is OK
-
-
duyn9uyen almost 8 yearsIs there anyway you can send me a copy of this solution? I literally copied the code but it doesn't seem to work in my solution. So I think that have some configuration that is different. My project properties has the project running under IIS Express under a port number. The project url has this "localhost:50892." I have tried using the port and without the port number in my unit test but no success. Still 404.
-
Nkosi almost 8 yearsWith in-memory testing requests are not actually hitting the network, so there is nothing to do with IIS or IIS Express.
HttpServer
aDelegatingHandler
that is handling the request that theHttpClient
is making. There is no need for a port and the default ishttp://localhost
like in my examples. I took the exact code from your post, only changed the host of the url and configured the controller to use attribute routing. -
Nkosi almost 8 yearsCopy the exact unit test class as it is in my answer and run it in your test project. It should run and pass. You can then review how it was done and try to apply the same technique to you actual test. There was nothing extra. Everything you see there is exactly how it was written and tested.
-
duyn9uyen almost 8 yearsI have uploaded the sample project here. I copied your code into the unit test. Does the unit test run successfully for you? I get a 404 with both tests. dropbox.com/s/0toamkkkrd50juq/WebApi_2.0_Sample.zip?dl=0
-
Nkosi almost 8 yearsI get green (test passes) every time i run it. If you have a GitHub account post the project up there and I'll take a look at your code. Downloading zip files is an issue for me. (security)
-
duyn9uyen almost 8 yearsI have uploaded the project to my github. github.com/duyn9uyen/webapi2_0_In_memory_host_unit_test1
-
Nkosi almost 8 yearsOk i downloaded it and was able to reproduce the errors your were getting. When i changed
config.MapHttpAttributeRoutes()
to useWebApiConfig.Register(config);
it started to work. Rather strange for me because in my other projects it works fine with just the map attribute routing, even with the controller in another project. -
duyn9uyen almost 8 yearsChanging config.MapHttpAttributeRoutes() to use WebApiConfig.Register(config) worked! Thank you so much. Any ideas why config.MapHttpAttributeRoutes() doesn't work?
-
Nkosi almost 8 yearsMy guess is that
MapHttpAttributeRoutes
searches the current assembly for ApiControllers that have the Route specific attributes. When I copied theProductController
from the Sample project over to the test project it worked. I'll probably have to take a look at the source on github or codeplex and see what that method does. -
GerjanOnline almost 7 yearsI just had the exact same problem, when I only called
config.MapHttpAttributeRoutes()
my routes/controllers are not discovered. When I add an explicit reference to my WebApi project by calling for exampleWebApiConfig.Register(config);
, it works! (only addingvar ctrl = new ValuesController();
also works!) My guess is that is has something to do with assemblies that are not used, are not loaded by the runtime. Which means that the WebApi is not loaded so the routes/controllers are not scanned and discovered -
Kyle Vassella over 5 yearsIs the point if using in-memory so that you don't have to create a duplicate Integration Test database? Or am I missing the point and you had to create one of those anyway? (this involves initializing integration test db, then subsequently destroying it for each integration test that is run)
-
kuldeep about 4 yearsis there no way I can debug my API while running an integration test ? I put break points in my webapi controller, but they do not seems to get a hit. However test passes !!