Search code examples
c#asp.netintegration-testingasp.net-web-api-routingattributerouting

ASP.NET Unit testing doesn't map routes


I have a web API project that I want to test with unit tests.

In the test class I create a HttpClient that is supposed to redirect the HTTP calls directly to my controllers:

public static HttpClient GetInMemoryServerClient(HttpConfiguration serverConfig)
{
    var config = serverConfig ?? new HttpConfiguration();
    MyService.WebApiConfig.Register(config, isTest: true);
    var server = new HttpServer();
    server.Configuration.Services.Replace(typeof(IHttpActionInvoker), server.Configuration.Services.GetActionInvoker());
    return new HttpClient(server) { BaseAddress = new Uri("http://fakehost:9999/") };
}

In the service project I have this class that calls config.MapHttpAttributeRoutes(), which works in prod but not when called by the test:

public class WebApiConfig{
    //...
    public static void Register(HttpConfiguration config) // this is called in Global.asax.cs
    {
        Register(config, false);
    }

    public static Register(HttpConfiguration config, bool isTest){
        //... various config and dependency injection setup here
        config.MapHttpAttributeRoutes();
    }
}

My controllers use attribute routing:

[RoutePrefix("api/groups/{groupId}/sets/{setId:minlength(1)}/items")]
public class ItemsController : BaseController
{
    [Route("{itemId:minlength(1)}")]
    [HttpGet]
    public async Task<API.Item> Get(string groupId, string setId, string itemId)
    {...}
}

When I use the HttpClient to make a call I get

{"Message":"No HTTP resource was found that matches the request URI 'http://fakehost:9999/api/groups/abc/sets/xyz/items/item123'."}

and inspecting the httpClient with the debugger I see httpClient.handler.Configuration.Routes.Count == 0, so mapping the routing attributes didn't work

Q: What is the correct way to set up the fake HttpClient so my unit tests can use it to call the controllers?


Solution

  • HttpConfiguration config is not being assigned to the server so the settings are not being applied. The method applied the mapping to the config but didn't assign the config to the server when it was initialized.

    public static HttpClient GetInMemoryServerClient(HttpConfiguration serverConfig = null) {
        var config = serverConfig ?? new HttpConfiguration();
        MyService.WebApiConfig.Register(config, isTest: true);
        var server = new HttpServer(config); //<-- pass config to server        
        var client = new HttpClient(server);
        //Server defaults to localhost so setting client to localhost as well
        client.BaseAddress = new Uri("http://localhost"); 
        return client;
    }
    

    In the test the client can then make a call like

    using(var client = GetInMemoryServerClient()) {
        var url = "api/groups/abc/sets/xyz/items/item123";
        var response = await client.GetAsync(url);
        //...do something with response
    }