21

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.

14
  • 2
    might not make a difference, but why not use GetAsync directly? Commented May 27, 2016 at 7:04
  • Are you using OWIN pipeline with start up? You may need to use TestServer. blogs.msdn.microsoft.com/webdev/2013/11/26/… Commented May 27, 2016 at 9:14
  • I am not using owin. Commented May 27, 2016 at 11:14
  • what version of asp.net-mvc web api are you using? Commented May 27, 2016 at 12:54
  • 2
    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. Commented May 27, 2016 at 13:30

1 Answer 1

30

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
Sign up to request clarification or add additional context in comments.

12 Comments

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.
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.
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.
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.
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
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.