Search code examples
asp.net-web-apicastle-windsorowinasp.net-web-api-routingself-hosting

Owin Testing (Self hosting) not registering web api routes when using DI


I am trying to implement Microsoft.Owin.Testing/3.0.1 for WebApi 2 integration tests. Everything works fine except when I add attribute routing. The routes are not recognized or I am unable to register them properly. I'm pretty sure it has something to do with api controller dependency injection that is not used when testing.

My values api controller

 [RoutePrefix("api")]
        public class ValuesApiController : ApiController
        {
            private readonly ValuesControllerService _valuesControllerService;

            public ValuesApiController(ValuesControllerService valuesControllerService)
            {
                _valuesControllerService = valuesControllerService;
            }

            // GET api/values
            [HttpGet]
            [Route("values")]
            public async Task<IHttpActionResult> Get()
            {
                if (_valuesControllerService == null)
                    throw new ArgumentException("valuesControllerService");

                return Ok(new[] { "value1", "value2" });
            }
        }

I am using Castle Windsor to resolve API Controllers using (source):

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorCompositionRoot(_container));
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_container));

I'm registering my WebApi routes in Global.asax so it is registered properly when running the application (and not the tests).

public class WebApiApplication : HttpApplication
    {
        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }

When testing, I call the registration after Startup. Here is a sample test which I use (test source):

[Test]
        public async void GetValueTestForStartup1()
        {
            using (var server = TestServer.Create<Startup1>())
            {
                using (var client = new HttpClient(server.Handler))
                {
                    var request = await client.GetAsync(_url);

                    Assert.IsTrue(request.IsSuccessStatusCode);
                }
            }
        }

And here are some Startup implementations I tried:

internal class Startup1
        {
            public void Configuration(IAppBuilder app)
            {
                new Startup().ConfigureForIntegrationTests(app, x => x.Kernel.ComponentModelBuilder.AddContributor(new SingletonEqualizer()));
                GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new TestWebApiResolver());
                GlobalConfiguration.Configure(WebApiConfig.Register);
            }
        }

404 when executing a request

internal class Startup2
{
    public void Configuration(IAppBuilder app)
    {
        new Startup().ConfigureForIntegrationTests(app, x => x.Kernel.ComponentModelBuilder.AddContributor(new SingletonEqualizer()));
        GlobalConfiguration.Configure(WebApiConfig.Register);
        GlobalConfiguration.Configuration.EnsureInitialized();
    }
}

System.InvalidOperationException : This method cannot be called during the application's pre-start initialization phase.

internal class Startup3
    {
        public void Configuration(IAppBuilder app)
        {
            new Startup().ConfigureForIntegrationTests(app, y => y.Kernel.ComponentModelBuilder.AddContributor(new SingletonEqualizer()));
            // Web API routes

            GlobalConfiguration.Configuration.MapHttpAttributeRoutes();

            GlobalConfiguration.Configuration.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            app.UseWebApi(GlobalConfiguration.Configuration);
            GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new TestWebApiResolver());
        }
    }

500 Internal Server error (probably because there is no parameterless constructor found).

Complete sourceproject is located here: https://github.com/jvanderbiest/OwinTestingWebApi


Solution

  • There is a similar SO question here about AutoFac. I must have looked over that response but the key to all of this is to register everything with your own HttpConfiguration instead of using the GlobalConfiguration object.

    I have updated the github code so the test is working fine now!