Unit Testing / Integration Testing Web API with HttpClient in Visual Studio 2013

40,789

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
Share:
40,789

Related videos on Youtube

duyn9uyen
Author by

duyn9uyen

Learning is doing.

Updated on September 16, 2020

Comments

  • duyn9uyen
    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 a HttpServer 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 a HttpConfiguration passed into HttpClient? I have seen many examples that use this.

    • Alexandru Marculescu
      Alexandru Marculescu almost 8 years
      might not make a difference, but why not use GetAsync directly?
    • Nkosi
      Nkosi almost 8 years
      Are you using OWIN pipeline with start up? You may need to use TestServer. blogs.msdn.microsoft.com/webdev/2013/11/26/…
    • duyn9uyen
      duyn9uyen almost 8 years
      I am not using owin.
    • Nkosi
      Nkosi almost 8 years
      what version of asp.net-mvc web api are you using?
    • Nkosi
      Nkosi almost 8 years
      During unit test there is not host for the HttpServer. which is why when you try to access the URL you get a 404.
    • duyn9uyen
      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
      Fran almost 8 years
      Where 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
      Nkosi almost 8 years
      Take 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
      duyn9uyen almost 8 years
      Adding 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
      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
      duyn9uyen almost 8 years
      @Nkosi, I have added some updates. Thanks for checking.
    • Nkosi
      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
      Nkosi almost 8 years
      @duyn9uyen check updated answer and comments. got your code to work.
    • Blue Clouds
      Blue Clouds about 6 years
      Why accessing data is not mentioned in the solution. We do not call an api just to check if the status is OK
  • duyn9uyen
    duyn9uyen almost 8 years
    Is 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
    Nkosi almost 8 years
    With in-memory testing requests are not actually hitting the network, so there is nothing to do with IIS or IIS Express. HttpServer a DelegatingHandler that is handling the request that the HttpClient is making. There is no need for a port and the default is http://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
    Nkosi almost 8 years
    Copy 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
    duyn9uyen almost 8 years
    I 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
    Nkosi almost 8 years
    I 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
    duyn9uyen almost 8 years
    I have uploaded the project to my github. github.com/duyn9uyen/webapi2_0_In_memory_host_unit_test1
  • Nkosi
    Nkosi almost 8 years
    Ok i downloaded it and was able to reproduce the errors your were getting. When i changed config.MapHttpAttributeRoutes() to use WebApiConfig.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
    duyn9uyen almost 8 years
    Changing config.MapHttpAttributeRoutes() to use WebApiConfig.Register(config) worked! Thank you so much. Any ideas why config.MapHttpAttributeRoutes() doesn't work?
  • Nkosi
    Nkosi almost 8 years
    My guess is that MapHttpAttributeRoutes searches the current assembly for ApiControllers that have the Route specific attributes. When I copied the ProductController 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
    GerjanOnline almost 7 years
    I 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 example WebApiConfig.Register(config);, it works! (only adding var 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
    Kyle Vassella over 5 years
    Is 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
    kuldeep about 4 years
    is 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 !!