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

duyn9uyen picture duyn9uyen · May 27, 2016 · Viewed 37.1k times · Source

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.

Answer

Nkosi picture Nkosi · May 29, 2016

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