Search code examples
c#asp.net-web-apiintegration-testingowinself-hosting

OWIN Startup class is being executed along with WebAPIConfig.Register method


I am using Microsoft.Owin.Hosting in my one of integration test project to self-host the Web API in order to test end to end functionality.

[TestMethod]
public void GetLoanApplications()
{
    using (WebApp.Start<Startup>(url: url))
    {
        using (var client = new HttpClient())
        {
            // Create httpclient and send request-and-response-metadata-in-aspnet-web-api
        }
    }
}

I am able to self-host the web API and able to invoke the controller action. Owin requires some Startup class configuration, which is as follows:

[assembly: OwinStartup(typeof(MyService.App_Start.Startup))]
namespace MyService.App_Start
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();

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

Here is my Web API Config method looks like:

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

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

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

Problem

  • When I run my main application (not the test project) both the Owin startup and web API config methods are getting called.
  • Let's say if I have some services or filters to be configured, it will get invoked twice.
  • What I thought is IF I am running test project it should only invoke owin startup file (which it is doing right now) and when I am debugging my main app it should only call web API config register method.

Any idea on is this the way it should work or I am doing something wrong?


Solution

  • First the system would need a way to differentiate between the environments.

    <appSettings>
        <add key="APP_ENVIRONMENT" value="Development" />
        <!-- ... -->
    </appSettings>
    

    Since when run in production both the Owin startup and web api config methods are getting called then OWIN is already configured as a first class citizen in the project.

    I suggest moving the code from Application_Start into owin start up

    public class WebApiApplication : System.Web.HttpApplication {
        protected void Application_Start() {
            //REMOVE THIS AND LET OWIN STARTUP HANDLE SETUP
            //GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
    

    so that it is only invoked once depending on the configured environment.

    [assembly: OwinStartup(typeof(MyService.App_Start.Startup))]
    namespace MyService.App_Start {
        public class Startup {
            const string EnvironmentKey = "APP_ENVIRONMENT";
            const string PRODUCTION = "Production";
            const string TEST = "Test";
    
            public void Configuration(IAppBuilder app) {
                string ENVIRONMENT = ConfigurationManager.AppSettings[EnvironmentKey] 
                                        ?? Production;
                if(ENVIRONMENT == TEST) {
                    var config = new HttpConfiguration();
                    WebApiConfig.Register(config);
                    app.UseWebApi(config);
                } else {
                    GlobalConfiguration.Configure(WebApiConfig.Register);
                }
            }
        }
    }
    

    Note the reuse of the WebApiConfig.Register to avoid repeated code. The same configuration will be applied for either environment and all in the same place if additional configurations were to be implemented (ie Development, Testing, Staging, Procustion...etc)

    The test project app.config would include the current environment's setting

    <appSettings>
        <add key="APP_ENVIRONMENT" value="Test" />
        <!-- ... -->
    </appSettings>
    

    and invoke the start up configuration for self-hosting otherwise it will default back to production settings, but would have also been configured in the web.config

    <appSettings>
        <add key="APP_ENVIRONMENT" value="Production" />
        <!-- ... -->
    </appSettings>