Advertisements
//
you're reading...
Programming

Integration Test ASP.NET Web API with StructureMap

One of the best new features of ASP.NET Web API is the ability to self host. This means that inside of our integration test, we can create a web server in memory, work with the web server through a web client (in memory as well), and not have IIS running on the box or on some other server to test. It’s all done “in-house”.

I’m using Nunit for testing and StructureMap for Inversion of Control (IOC).  I’ve got two projects. The first is a MVC 4 Application with a Web API ApiController. The second project is a standard test project with Nunit. I’m on Visual Studio 11 on Windows 7. Let’s dig in. First, the controller I’m trying to integration test:

public class DrinkLogsController : ApiController
{
	private readonly IDrinkTrackerContext _context;

	public DrinkLogsController(IDrinkTrackerContext context)
	{
		_context = context;
	}

	[ValidationActionFilter]
	public HttpResponseMessage PostDrinkLog(DrinkLog value)
	{
		.
		.
		.
		// Code for the post handler goes here. It's not relevant to the discussion at hand.

	}

}

So what do we have? A typical ApiController for a entity called DrinkLogs. This controller is taking in an IDrinkTrackerContext which in reality is actually an instance of a DbContext. But since we’re using IoC and interfaces, the controller doesn’t really know much about the implementation of what we’ve passed in. No tight coupling here! There’s a POST handler that’s taking in a new DrinkLog and doing something with. We don’t care what it’s doing really. At least not for the purposes of this blog entry. Now let’s look at the test harness.

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
using System.Web.Http.SelfHost;
using DrinkTracker.Core.Model;
using DrinkTracker.WebAPI;
using DrinkTracker.WebAPI.Controllers;
using DrinkTracker.WebAPI.Filters;
using NUnit.Framework;
using Newtonsoft.Json;

namespace DrinkTracker.Test

{
	[TestFixture]
	public class DrinkLogApiIntegrationTest
	{

		[Test]
		public void Post_Drink_Log_With_No_Drink_Failure()
		{
			var baseAddress = "http://localhost:8080/";

			var selfHostConfig = new HttpSelfHostConfiguration(baseAddress);

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

			selfHostConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

			var container = IoC.Initialize();
			var resolver = new SmDependencyResolver(container);

			selfHostConfig.ServiceResolver.SetResolver(resolver.GetService, resolver.GetServices);

			var server = new HttpSelfHostServer(selfHostConfig);
			var client = new HttpClient();
			server.OpenAsync().Wait();

			client.BaseAddress = new Uri(baseAddress);

			var newLog = new DrinkLog { PersonId = 1, LogDate = DateTime.UtcNow };

			var postData = new StringContent(JsonConvert.SerializeObject(newLog), Encoding.UTF8, "application/json");

			var r = client.PostAsync("api/drinklogs", postData);

			Assert.That(r.Result.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));

		}

	}
}

Before I get too far explaining this test, it’s imperative to note that in order to use self hosting, you need to setup an http url namespace resolution. This is described in detail over on the Asp.Net website. Go read that, do what it says, and come back.

Too lazy to go check it out? Here’s what it says: you need to open up a developer command prompt and execute the following command (with elevated privledges):

netsh http add urlacl url=http://+:8080/ user=machine\username

Now let’s look at the test code. Right off the bat we’re setting up a location for the server to listen at. Then, using that address, we can create a HttpSelfHostConfiguration. This is where we’ll put all the secret sauce. You’ll need to do all your MVC setup work that would normally be done in the Global.asax or in the App_Start folders (where StructureMap for MVC is setup) of your MVC project. Easy enough to just go look at your MVC project and see what you’re setting up. In my case, I’m registering an ActionFilter and a Route. Set this here because the Global.asax is part of the ASP.NET runtime which we’re not really going through. I also set the IncludeErrorDetailPolicy to always to make sure I get back detailed information about any problems.

Now we move on to the IoC. Like I said, I’m using StructureMap because that’s what I’ve been using for MVC for a couple years. You could use your own flavor of IoC such as Ninject or Unity, but what’s important in this code snippet is that you get a reference to an IDependencyResolver.

Lets assume that I’ve got StructureMap setup as I want it inside the MVC project. In this case, calling IoC.Initialize() will setup StructureMap just as its setup in the MVC project. The SmDependencyResolver gives an IDependencyResolver we so longingly need which we then use to set the ServiceResolver on the HttpSelfHostConfiguration.

The hard part is done now. It’s all smooth sailing from here on out. We setup the HttpSelfHostServer passing it the configuration we so lovingly crafted earlier. Use server.OpenAsync().Wait() to put the server into a zen state of acceptance of all connections on the address we specified earlier.

Then it’s just standard HttpClient stuff. In this case, passing in a JSON representation of the new DrinkLog. Since this test is specifically testing to make sure that if the caller forgets a piece of data on a POST, we return a BadRequest HttpStatusCode. Once that’s tested, we’re golden. Or, more appropriately, all green.

If your Result.StatusCode is 500, Internal Server Error, check the error detail. If you see an error like “No parameterless constructor defined for this object.”, then your IoC isn’t setup correctly. Set a breakpoint in your constructor to confirm to yourself that you’re not setup correctly. This is ususally caused by not telling the IoC system about your bindings. Fix that, try again. If it still doesn’t work, ask a question.

Happy Web APIing.

Advertisements

Discussion

2 thoughts on “Integration Test ASP.NET Web API with StructureMap

  1. hi, here is a tip for you:

    –> var client = new HttpClient();
    the HttpClient has to be initialized with server
    –> var client = new HttpClient(server);

    that way you can go over the memory instead over the http 🙂

    you can look at http://pfelix.wordpress.com/2012/03/05/asp-net-web-api-in-memory-hosting/ for more info

    Posted by SeriousM | April 11, 2012, 9:08 am
  2. Thanks. I’m not sure that change does anything, however as I’m using an HttpSelfHostServer rather than a typical HttpServer as referenced by your link. In fact, on that same blog, a couple days later, Pedro went over how to use the HttpSelfHostServer:

    http://pfelix.wordpress.com/2012/03/08/asp-net-web-api-self-hosting/

    I believe the HttpSelfHostServer users an entirely different transport mechanism.

    Posted by Kenstone | April 11, 2012, 10:06 am

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

Advertisements

I'm Ken Stone. I'm an indie developer with a focus on .NET and Windows Phone development. I have a day job, but everything I say is mine alone.

TechEd 2012July 11th, 2012
Let the learning begin.
%d bloggers like this: