IIS-Free ASP.NET MVC Web API Integration Testing

Introduction

If you’re like me and think running an IIS server (including IIS Express and the Visual Studio web server) just to integration test your web API is a pain, please read on. In this post, I describe how to self-host a web api. Fortunately the design of ASP.NET MVC Web API makes self-hosting easy.

The Web API

Listing 1 shows the code for the simple web API used in this post. It consists of a single controller implementing a POST method. The method adds a car advertisement to the site. Notice there is no default constructor which can only mean we’re going to talk about dependency injection at some point.

public class AdvertisementController : ApiController
    {
        private readonly ICarSiteRepository repository;

        public AdvertisementController(ICarSiteRepository repository)
        {
            this.repository = repository;
        }

        public HttpResponseMessage Post(HttpRequestMessage request, Advertisement advertisement)
        {
            this.repository.AddAdvertisement(advertisement);
            var response = request.CreateResponse(HttpStatusCode.Created);
            response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = Guid.NewGuid().ToString("N") }));
            return response;
        }
    }

Listing 1

The Repository

The ICarSiteRepository defines the contract for adding advertisements to the site. It consists of a single method which takes an Advertisement instance. The interface’s definition is shown in Listing 2.

public interface ICarSiteRepository
    {
        void AddAdvertisement(Advertisement adverstisement);
    }

Listing 2

The Model

The Advertisement class is the only model class. Its code is shown in Listing 3.

public class Advertisement
    {
        public Guid Id { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
        public int Year { get; set; }
        public string Description { get; set; }
    }

Listing 3

Now we’ll cover testing the controllers Post method.

The Self-hosted HTTP Server

In order to test web API methods without IIS, we will need to write an HTTP server. That sounds like more trouble than it is worth, but it is actually quite easy. Keep in mind that we want to have the minimum functionality that will allow us to test web API methods. Listing 4 shows the code.

public class SelfHostHttpServer : IDisposable
    {
        private bool disposed;
        private HttpSelfHostServer server;
       
        public SelfHostHttpServer(Action<HttpConfiguration> configurationHandler, string baseUrl)
        {
            if (configurationHandler == null)
            {
                throw new ArgumentNullException("configurationHandler", "Value cannot be null or empty.");
            }

            if (string.IsNullOrWhiteSpace(baseUrl))
            {
                throw new ArgumentException("Value cannot be null or empty.", "baseUrl");
            }

            this.InitializeServer(configurationHandler, baseUrl);
        }

        public HttpResponseMessage Send(HttpRequestMessage request)
        {
            return new HttpClient().SendAsync(request).Result;
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (this.server != null)
                    {
                        this.server.Dispose();
                        this.server = null;
                    }
                }

                this.disposed = true;
            }
        }

        private void InitializeServer(Action<HttpConfiguration> configurationHandler, string baseUrl)
        {
            var config = new HttpSelfHostConfiguration(baseUrl);
            configurationHandler(config);
            this.server = new HttpSelfHostServer(config);
            this.server.OpenAsync().Wait();
        }

Listing 4

The interesting code is in the InitializeServer method. All you need to do in order to get a functional HTTP server is do the following:

  1. Create an instance of the HttpSelfHostConfiguration class.
  2. Call the static WebApiConfig.Register method. This is done via a delegate for reusability.
  3. Create an instance of the HttpSelfHostServer class, passing in the HttpSelfHostConfiguration instance.
  4. Call OpenAsync to make the server listen for requests.

Our tests will call the server’s Send method when a request needs to be sent to the server. Send simply creates an HttpClient instance, sends the request to the server, and sends the response back.

The Test

Test test class is shown in Listing 5.

[TestClass]
    public class When_Calling_The_AdvertisingController
    {
        private const string BaseUrl = "http://localhost/baseUrl/";
        private static MediaTypeFormatter formatter = new JsonMediaTypeFormatter();
        private static SelfHostHttpServer server;
        private TestContext testContextInstance;

        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [ClassInitialize]
        public static void ClassInitialize(TestContext testContext)
        {
            server = new SelfHostHttpServer(WebApiConfig.Register, BaseUrl);
            Assert.IsNotNull(server);
        }

        [TestMethod]
        public void The_New_Advertisement_Is_Added()
        {
            var advertisement = new Advertisement
                                    {
                                        Year = 2013,
                                        Make = "Audi",
                                        Model = "S8",
                                        Description = "Check it out!"
                                    };

            var request = new HttpRequestMessage(HttpMethod.Post, BaseUrl + "/api/advertisement")
                              {
                                  Content = new ObjectContent<Advertisement>(advertisement, formatter)
                              };

            var response = server.Send(request);
            this.TestContext.WriteLine("Response content:{0}{1}", Environment.NewLine, response.Content.ReadAsStringAsync().Result);
            Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
        }
    }

Listing 5

The single test method creates a request, sends it to the HTTP server, and verifies the response. At this point the test will fail. The response’s content contains the following JSON:

{
  "Message": "An error has occurred.",
  "ExceptionMessage": "Type 'CarSite.Api.Controllers.AdvertisementController' does not have a default constructor",
  "ExceptionType": "System.ArgumentException",
  "StackTrace": "   at System.Linq.Expressions.Expression.New(Type type)\r\n   at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}

The response should come as no surprise since the API has not been given an ICarSiteRepository. We can easily mock this but how do we get it to the controller? We’ll need to associate an IDependencyResolver implementation to the HttpSelfHostConfiguration. We will use Microsoft Unity’s dependency resolver and container.

Unity.WebAPI NuGet Package

I install this package whenever I need to incorporate Unity in my web API projects. However, this is not a good idea when self-hosting. See this post for the trouble it causes. Fortunately, Unity itself has the UnityDependencyResolver class which is an implementation of IDependencyResolver.

By the way, install a mocking framework as well via NuGet. We will pass a mocked implementation of ICarSiteRepository. I am going to use RhinoMocks.

Microsoft Unity NuGet Package

Search NuGet for “Unity” and install it. Using Unity with our self-hosted server is simply a matter of configuring a UnityContainer instance, registering the types, passing it to a UnityDependencyResolver instance, and associating the resolver with the server’s HttpSelfHostConfiguration.

We’re going to inject the IDependencyResolver into the HTTP server so change the code so it looks like Listing 6.

private bool disposed;
        private HttpSelfHostServer server;
       
        public SelfHostHttpServer(Action<HttpConfiguration> configurationHandler, string baseUrl, IDependencyResolver dependencyResolver)
        {
            if (configurationHandler == null)
            {
                throw new ArgumentNullException("configurationHandler", "Value cannot be null or empty.");
            }

            if (string.IsNullOrWhiteSpace(baseUrl))
            {
                throw new ArgumentException("Value cannot be null or empty.", "baseUrl");
            }

            this.InitializeServer(configurationHandler, baseUrl, dependencyResolver);
        }

        public HttpResponseMessage Send(HttpRequestMessage request)
        {
            return new HttpClient().SendAsync(request).Result;
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (this.server != null)
                    {
                        this.server.Dispose();
                        this.server = null;
                    }
                }

                this.disposed = true;
            }
        }

        private void InitializeServer(Action<HttpConfiguration> configurationHandler, string baseUrl, IDependencyResolver dependencyResolver)
        {
            var config = new HttpSelfHostConfiguration(baseUrl);

            if (dependencyResolver != null)
            {
                config.DependencyResolver = dependencyResolver;
            }

            configurationHandler(config);
            this.server = new HttpSelfHostServer(config);
            this.server.OpenAsync().Wait();
        }
    }

Listing 6

As we’re going to create the dependency resolver in the test class, we’ll need to install the Unity bootstrapper for ASP.NET Web API package via NuGet because the Unity package doesn’t contain the assembly we need. Search NuGet for “Unity bootstrapper” and install.

Edit the test class’s ClassInitialize method so it looks like Listing 7.

[ClassInitialize]
        public static void ClassInitialize(TestContext testContext)
        {
            var container = new UnityContainer();
            
            container.RegisterInstance(
                typeof(ICarSiteRepository),
                MockRepository.GenerateMock<ICarSiteRepository>(),
                new HierarchicalLifetimeManager());

            var resolver = new UnityDependencyResolver(container);

            server = new SelfHostHttpServer(WebApiConfig.Register, BaseUrl, resolver);
            Assert.IsNotNull(server);
        }

Listing 7

Run the test and it should pass. You now have an infrastructure that allows you to test your web API without needing IIS!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s