How To Write Testable ASP.NET UserControls
Posted by Davy Brion on October 11th, 2008
Ever since i wrote my How To Write Testable ASP.NET WebForms post i’ve had people asking me how to make it work with UserControls. I pretty much avoided UserControls with this approach for as long as i could, but for our current project we really had a need for it. So i started implementing this together with a coworker, and this is the solution we came up with.
Note: if you haven’t read that post on how to write testable ASP.NET WebForms, be sure to read it first because this approach is very similar and i won’t repeat all of the general concepts of the approach here.
In the implementation for WebForms, we call the pages Views, and they all implement their own interface which inherits from our own IView interface. Now we wanted something that would work both with UserControls and Web Parts. So we figured we should call both of them ViewParts and we have the following base interface that each ViewPart should implement:
public interface IViewPart
{
bool IsPostBack { get; }
IDictionary State { get; }
}
If you need more operations that each ViewPart should be able to offer, you can obviously just add whatever you want to this interface.
We also want each ViewPart to have its own Controller, which we call PartControllers. Since we want to be able to test both the ViewParts and their containing Views in isolation, each View’s Controller can never communicate directly with the specific PartController(s) of the ViewPart(s) that it contains. So we first need the following base interface for each PartController:
public interface IPartController
{
IDispatcher Dispatcher { get; set; }
void AddInitialRequests();
void GetInitialResponses();
}
In our case, each PartController will need an IDispatcher instance to be able to communicate with our Request/Response Service Layer. So the IDispatcher reference is only necessary if you’re using that as well. The idea is that the containing View’s Controller needs to provide its IDispatcher instance to each contained ViewPart’s PartController so the initial requests of each ViewPart can be sent to the service layer together with whatever initial requests the containing View’s Controller needs to send when the View is loaded. That’s basically what the AddInitialRequests and GetInitialResponses methods are for. Again, this is very specific to the usage of the Request/Response Service Layer, so you might want to put some entirely different basic operations in your base IPartController interface.
We need to be able to ask each ViewPart for its typed PartController, so we also have this interface:
public interface IViewPart<TPartController> : IViewPart
where TPartController : IPartController
{
TPartController GetPartController(IDispatcher dispatcher);
}
We also need to be able to ask each PartController for a typed instance of its ViewPart, so we also have the following interface:
public interface IPartController<TViewPart> : IPartController
where TViewPart : IViewPart
{
TViewPart ViewPart { get; }
}
And then our PartController base class looks like this:
public abstract class PartController<TViewPart> : IPartController<TViewPart> where TViewPart : IViewPart
{
protected PartController(TViewPart viewPart)
{
ViewPart = viewPart;
}
public TViewPart ViewPart { get; private set; }
public IDispatcher Dispatcher { get; set; }
public virtual void AddInitialRequests() {}
public virtual void GetInitialResponses() {}
}
Now, when we want to write UserControls that can work with this approach, we need to inherit from the following UserControl base class:
public class UserControl<TPartController> : System.Web.UI.UserControl, IViewPart<TPartController>
where TPartController : IPartController
{
protected TPartController Controller { get; private set; }
protected UserControl()
{
Controller = IoC.Container.Resolve<TPartController>(new { ViewPart = this });
}
public TPartController GetPartController(IDispatcher dispatcher)
{
Controller.Dispatcher = dispatcher;
return Controller;
}
public IDictionary State
{
get { return ViewState; }
}
}
When the UserControl is constructed, we retrieve an instance of the specific PartController through the IOC container, and we pass the newly created instance of our UserControl as the ViewPart dependency of the PartController.
What you’ve seen so far is all very abstract, so let’s go over a small example. Suppose we have a View (DummyPage) which contains a ViewPart (DummyPart). First, let’s create the DummyPart:
public partial class DummyPart : UserControl<IDummyViewPartController>, IDummyViewPart
{
}
Our DummyPart (the ViewPart) inherits from our UserControl base class and passes the interface type of our PartController as the type parameter of the UserControl. It also implements the IDummyViewPart interface (which is empty in this simple example):
public interface IDummyViewPart : IViewPart<IDummyViewPartController>
{
}
The IDummyViewPartController interface looks like this:
public interface IDummyViewPartController : IPartController<IDummyViewPart>
{
void SomeSpecificOperationForTheDummyViewPart();
}
The implementation of the DummyPartController looks like this:
public class DummyViewPartController : PartController<IDummyViewPart>, IDummyViewPartController
{
public DummyViewPartController(IDummyViewPart viewPart) : base(viewPart) {}
public override void AddInitialRequests()
{
// add some initial requests to the IDispatcher
}
public override void GetInitialResponses()
{
// retrieve the responses for the initial requests from the IDispatcher
}
public void SomeSpecificOperationForTheDummyViewPart()
{
// do something specific to the IDummyViewPart
}
}
So what do we have now? A reusable UserControl which has its own controller where the actual logic of the UserControl will be implemented. We can write unit tests for all of the logic that the UserControl needs to have. We can also reuse this UserControl in a Page, and do so in a manner which enables us to fake the implementation of the UserControl for the tests we’ll write for the logic in that Page.
Suppose we have a DummyPage which contains the DummyPart UserControl. Our DummyPage implements the following interface:
public interface IDummyView : IView
{
IDummyViewPart DummyPart { get; }
}
The code of our actual DummyPage looks like this:
public partial class DummyPage : Page<DummyController>, IDummyView
{
protected void Page_Load(object sender, EventArgs e)
{
Controller.Load();
}
public IDummyViewPart DummyPart
{
get { return dummyPart; }
}
}
And the code of the DummyController looks like this:
public class DummyController : Controller<IDummyView>
{
private IDummyViewPartController partController;
public DummyController(IDummyView view) : base(view)
{
}
public void Load()
{
partController = View.DummyPart.GetPartController(Dispatcher);
if (!View.IsPostBack)
{
partController.AddInitialRequests();
SendOurOwnRequestsAndGetTheResponses();
partController.GetInitialResponses();
}
}
private void SendOurOwnRequestsAndGetTheResponses()
{
// this method would send some requests through the IDispatcher and
// retrieve the responses
}
}
In the Load method of the DummyController, we retrieve the DummyPartController and we can communicate with it. And since we’re talking to an interface type, we can easily provide a mocked IDummyPartController instance for our unit tests.
This approach makes it possible to create UserControls which you can easily write unit tests for, and you can reuse the UserControls in containing pages while remaining the flexibility to write unit tests for those containing pages without being dependent on the actual implementation of the UserControl.
October 12th, 2008 at 3:05 pm
[...] How To Write Testable ASP.NET UserControls – Davy Brion ‘ This approach makes it possible to create UserControls which you can easily write unit tests for (…) ‘ [...]
October 13th, 2008 at 12:22 am
[...] How to Write Testable ASP.NET User Controls (Davy Brion) [...]
October 13th, 2008 at 11:14 am
[...] How To Write Testable ASP.NET UserControls – Davy Brion follows on from a past post about writing testable webforms, with a look at doing the same with User Controls [...]