Pages

Sunday, October 27, 2013

Fluent controller builder updates

Yesterday I blogged about a fluent controller builder that I put together. You can read about it here. While building further tests using this controller builder I soon got into trouble. My initial design catered well for simple controllers which always have default constructors. So following code worked

var documentController = Build.Controller<DocumentController>()
                              .Having(b =>
                                {
                                    b.PostRequest().At("http://localhost/api/content");
                                    b.UsingDefaultRoute().HavingRouteData("controller", "document");
                                });

 A problem with this code is that it is creating an instance of the controller for me. This works fine when controller has a default constructor. As complexity of my application grew, I soon needed to inject various dependencies into my controller and above code stopped working. To fix the problem, I have introduced a new class that lets you configure a controller instance for use in unit testing. So you control the instantiation part and pass the instance of a configuration class that would do rest of the magic. Here is how the new code looks

documentRepository = Substitute.For<IRepository<Document>>();
idGenerator = Substitute.For<IGenerateId>();
urlBuilder = Substitute.For<IBuildUrl>();

documentController = new DocumentController(documentRepository, idGenerator, urlBuilder);

Configure.Controller(documentController).AsHaving(b =>
             {
                    b.PostRequest().At("http://localhost/api/content");
                    b.UsingDefaultRoute().HavingRouteData("controller", "document");
             });

Here my controller has three dependencies for which I want to inject stubs instead of real objects. This is easy because now I can create my own instance of controller and send it in to new Configure class that would prepare my controller for unit testing.

The nuget package has been updated to include this new change. You can find the details here

Saturday, October 26, 2013

Fluent controller builder for unit testing Web API controllers

This post is not about unit testing Web API controllers. A gentleman named Peter Provost has already written about this in enough details here. I actually followed his post to fix some issues with my unit tests. In order to unit test Web API controllers you need to set up a few things before you can start calling action methods.


  1. Populate Request property of controller with a new instance of HttpRequestMessageClass
  2. Define a route
  3. Create a route data collection
  4. Create an instance of HttpControllerContext using the above and use this to set ControllerContext property on our controller instance
  5. Tell the request object created in first step to use the above configuration.    
Does this sound complex? It does to me, so let me show you the code that actually does the above and you would know that this only sounds complex
private static void SetupControllerForTests(ApiController controller)
{
    var config = new HttpConfiguration();
    var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/products");
    var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
    var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });

    controller.ControllerContext = new HttpControllerContext(config, routeData, request);
    controller.Request = request;
    controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
}

This code is not very complex to understand but it has lot of moving parts. For example, I may not always test for a post request or I may not always use the same route. These things change based on the context of you test. So reusing this code in multiple tests is challenging. An easy way to fix the problem is to accept most things as parameters to the method. 

Welcome to the fluent world

Being a big follower of fluent way of writing code (read more about this here) I re-factored this code. Here is how the code for controller set up looks in fluent world
var documentController = Build.Controller<DocumentController>()
                              .Having(b =>
                                {
                                    b.PostRequest().At("http://localhost/api/content");
                                    b.UsingDefaultRoute().HavingRouteData("controller", "document");
                                });
Doesn't this read natural - "Build a controller named DocumentController having a port request at "http://localhost/api/content" and using default route and route data 'controller' having a value of 'document'". It indeed reads better and is easy to repeat in multiple tests with changing request types, routes and route data. 

But what is going on here. 

Lets analyse this code line by line. On first line we call  


Build.Controller<DocumentController>()
Build is a static class having a method Controller<T>. This gives us an instance of a class called ControllerBuilder<T>. Here is the code for both of these classes
public static class Build
    {
        public static ControllerBuilder<T> Controller<T>() where T : ApiController, new()
        {
            return new ControllerBuilder<T>();
        }
    }

public class ControllerBuilder<T> where T: ApiController, new()
    {
        private RequestBuilder requestBuilder;
        private RouteBuilder routeBuilder;

        public RequestBuilder PostRequest()
        {
            requestBuilder = new RequestBuilder(HttpMethod.Post);
            return requestBuilder;
        }

        public RouteBuilder UsingRoute(string name, string value)
        {
            routeBuilder = new RouteBuilder(name, value);
            return routeBuilder;
        }

        public RouteBuilder UsingDefaultRoute()
        {
            return UsingRoute("DefaultApi", "api/{controller}/{id}");
        }

        public T Having(Action<ControllerBuilder<T>> builder)
        {
            builder(this);
            return Build();
        }

        private T Build()
        {
            var configuration = new HttpConfiguration();
            var routeData = routeBuilder.BuildFor(configuration);
            var request = requestBuilder.Build();

            var controller = new T
            {
                ControllerContext = new HttpControllerContext(configuration, routeData, request),
                Request = request
            };
            controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = configuration;

            return controller;
        }
    }
There is a lot going on here..!!
 Actually not, it is same piece of code that we saw in the beginning in private method Build of ControllerBuilder class. All other methods are there in order to provide a fluent way of configuring and setting various pieces of information required to build the controller. There are two other classes referred here, namely RequestBuilder and RouteBuilder. They too are simple classes with methods that let you configure a HttpRequestMessage and route definitions in a fluent way. Here is how those classes look
public class RequestBuilder
    {
        private readonly HttpMethod httpMethod;
        private string requestUrl;

        public RequestBuilder(HttpMethod httpMethod)
        {
            this.httpMethod = httpMethod;
        }

        public RequestBuilder At(string url)
        {
            requestUrl = url;
            return this;
        }

        public HttpRequestMessage Build()
        {
            return new HttpRequestMessage(httpMethod, requestUrl);
        }
    }

public class RouteBuilder
    {
        private readonly string routeName;
        private readonly string routeValue;
        private readonly HttpRouteValueDictionary routeDataCollection = new HttpRouteValueDictionary();

        public RouteBuilder(string routeName, string routeValue)
        {
            this.routeName = routeName;
            this.routeValue = routeValue;
        }

        public RouteBuilder HavingRouteData(string name, string value)
        {
            routeDataCollection.Add(name, value);
            return this;
        }

        public IHttpRouteData BuildFor(HttpConfiguration config)
        {
            var route = config.Routes.MapHttpRoute(routeName, routeValue);
            return new HttpRouteData(route, routeDataCollection);
        }
    }
As you can see there is nothing complex in these classes, just a few methods that accept some parameters required to configure the object they are building and a method that actually builds the object.

In closing

Fluent interfaces, in my opinion, come in handy where an operation/task consists of series of steps that when written in traditional style feel unrelated. This is mostly found in the set up phase of actual operation. Hope you find this little tip helpful.

These files are also available for download as a nuget package named "FluentTestingUtils". If you are new to nuget then navigate to pacckage page on nuget to know more

Update

I just published an update to this package as I found out a tricky situation with using these classes. You can read the details here. I hope to push more and more updates in future.