Creating Testable Controllers in Umbraco

As a Tech Lead in my team, One of my responsibilities is making sure that all code entering our codebase is unit tested. I have been looking at the SurfaceController and I want to know what benifits it provides that a normal controller does not.

Let me be clear, I can see that it provides some useful properties like the Services property, but they do not help with the testability of our code base.

Imagine I would like to mock the ContentService; normally, using MVC, you would make IContentService a dependency of your controller and then in your test, you could send a mock instance, whilst relying on an IOC container to manage providing the real ContentService at runtime:

public class MySurfaceController : SurfaceController{

    private readonly IContentService _contentService;

    public MySurfaceController(IContentService contentService)
    {
        _contentService = contentService;
    }
}

public class Tests
{
    public void MySurfaceController_Scenario_Outcome()
    {
        var stubContentService = MyMockingFramework.GenerateStub<IContentService>();
        // do some mocking here.
        var controller = new MySurfaceController(stubContentService);
        //act and assert here.
    }
}

public class Global : UmbracoApplication
{
    // This is a Ninject Implementation
    protected override void OnApplicationStarted(object sender, EventArgs e)
    {
        var kernel = new StandardKernel();
        kernel.Bind<IContentService>().ToMethod(c => UmbracoContext.Current.Application.Services.ContentService);
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
        base.OnApplicationStarted(sender, e);
    }
}

By making the ContentService a protected property in the controller itself, there is no way to stub out the ContentService. The controller also becomes more complicated than it needs to be; one thing that has caught me in the past is when mocking the WebPageBase<T> object is that certain properties can be accessed in more than one place. The problem this caused, was that I had to know exactly how I was going to be reading the request from: Request is a property on this object, but so is Context which contains a reference to Request also, and when working with multiple developers, you need to be sure you cover every angle.

After seeing some of these properties, I feel like I need to remove them some way or another. I could override SurfaceController, override each of those properties and have them throw an exception, I could also warn my team not to use those properties and that would work. However, when working with code, I like to work with absolutes; if something could possibly go wrong, it will. I tried to convert my controller to a normal controller and use the normal MVC Html.Action(“action”, “controller”) method, but it claims that the routes are not configured properly to work. Also, I tried to use the Umbraco Html.Action<TController>(“action”) method, but it complains that TController must be a SurfaceController.

This is where I turn to you clever, clever people. Please, use the comment section below and help me understand the SurfaceController in its most naked form; and help me to understand why normal controllers won’t work alongside Umbraco; How could I alter the routing functionality to enable normal controllers.

I thank you in advance and I look forward to seeing your responses.

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