Tagged: ASP.NET MVC Framework

Unit Testing with Sitecore

This post is going to cover how you can create highly testable code for Sitecore, in particular when using the WebForms.

The State of the Art

I’m sure everybody reading this already knows Sitecore is a powerful and hugely-capable CMS. I’m also sure that everybody reading this knows it is incredibly difficult to write unit tests for code that interacts with Sitecore. Let’s quickly recap why testing is so difficult:

  1. WebForms encourages violation of Single Responsibility Principle through mixing business and UI logic in control code-behinds
  2. Lack of interfaces throughout Sitecore API
  3. Heavily dependent on Sitecore.Context, effectively a global variable
  4. Lack of strong typing on Items and Fields, meaning propogation of magic strings

This list can be divided into two categories: problems caused by WebForms itself (point 1) and problems caused by Sitecore (points 2-4). You can solve each category independently, but both need to be solved if you wish to achieve high unit test coverage. We’ll cover solutions separately, and put them together at the end.

How do you solve a problem like WebForms?

WebForms is control-based. Pages are composed of increasingly-complex controls, arranged into a control tree. All code for a particular control is found its code behind. It’s so easy to dump all your code in the code-behind and be done with it. This is why you frequently see mixed concerns in WebForms application code. But there’s got to be a better way, right? We could, with masses of self-discipline, ensure a clear demarcation between business and UI logic. We could even create some Models to supply data to the View (a control in the case of webforms). This sounds reasonable, but hang on!

We’re talking views and models here. Doesn’t this mean we’re just creating a disorganised MV* framework? If that is the case, why don’t we formalise it and introduce a well-defined framework with clear and consistent development patterns?

MV*

The typical approach for structuring web apps for testability/separation of concerns is Model-View-Controller (MVC). However, MVC does not sit well with WebForms’ evented, control-based approach. A much better fit is the Model-View-Presenter (MVP) pattern, a derivative of MVC.

In MVP the View is the entry point, and has a well-defined interface. Views delegate to Presenters through events. The View and the Model interact through two-way data-binding. The Presenter’s job is to encapsulate business logic, update the Model and respond to events from the View.

As WebForms offers eventing and two-way databinding (through ObjectDataSource), the MVP pattern is a perfect fit. Whilst it is possible to write your own MVP framework, it makes more sense to have an off-the-shelf product such as WebFormsMVP. WebFormsMVP is a mature, battle-hardened and open-source implementation of the MVP pattern. As well as the basic MVP pattern, WebFormsMVP has adapters for common Dependency Injection containers, automatically resolving dependencies for you; and ships with a pub/sub message bus allowing for decoupled, cross-presenter messaging.

It’s also worth noting that usage of WebFormsMVP is opt-in. You can use it where and when you like, as you see fit. It doesn’t force the paradigm on the entirety of your application.

Working with WebFormsMVP

Installation instructions can be found in the WebFormsMVP readme.

Let’s run through a scenario. Imagine we are trying to create the following form:

Basic web form

Without any real consideration of architecture, our code might look something like this:

<p runat="server" id="message" class="msg"></p>

<fieldset>
<legend>User details</legend>
<div>
<label for="firstName">First name</label>
<asp:TextBox runat="server" ID="firstName" />
</div>
<div>
<label for="lastName">Last name</label>
<asp:TextBox runat="server" ID="lastName" />
</div>
<div>
<label for="telephoneNumber">Telepone</label>
<asp:TextBox runat="server" ID="telephoneNumber" />
</div>
<asp:Button runat="server" OnClick="OnUserSubmit" Text="Add user"/>
</fieldset>

And the code behind:

public partial class AddUser : System.Web.UI.UserControl
{

protected void OnUserSubmit(object sender, EventArgs e)
{
var user = new User
{
FirstName = firstName.Text,
LastName = lastName.Text,
TelephoneNumber = telephoneNumber.Text
};

var repo = new InMemoryUserRepository();
var success = repo.Add(user);

if(success)
{
message.InnerText = "User added successfully";
message.AddClass("msg--success");
}
else
{
message.InnerText = "Something went wrong";
message.AddClass("msg--error");
}
}
}

Despite this being an incredibly simple form, we’re already falling into some classic WebForms traps:

  • Manually hydrating objects from the form values.
  • Handling business logic of adding people to a simple InMemoryRepository
  • Violating Single Responsibility Principle – handling reporting back to user when it should be someone else’s job.
  • And we haven’t even got validation in place yet!

Thankfully, all of these can be remedied with the assitance of WebFormsMVP.

We already have our view in the AddUser control, so we need to create a model (specifically for the view, don’t confuse with domain models such as User), an interface for the View, a Presenter, and an object to encapsulate data passed with events.

Our model is really simple, all the AddUser control needs to render is a success/failure message. In WebFormsMVP models are simple POCOs.

public class AddUserViewModel
{
public string Message { get; set; }
public FormResult Result { get; set; }
}

public enum FormResult
{
None,
Success,
Error
}

Next we need to create an interface for the View. Our View exposes functionality to add users to our repository, so naturally our View needs an AddingUser event. In WebFormsMVP, the View’s interface should extend IView or IView<TModel>. The IView<TModel> interface includes access to a strongly-typed Model property, and a basic Load event:

public interface IAddUserView : IView<AddUserViewModel>
{
event EventHandler<AddUserEventArgs> AddingUser;
}

WebFormsMVP leverages the standard .NET eventing patterns. Therefore if data is neeeded to be passed with events, you should extend the EventArgs object and add any data there:

public class AddUserEventArgs : EventArgs
{
public User User { get; set; }
}

Now our View has a clean Model, a well-defined interface to the View and the ability to pass a User object to any subscribers of the View’s events. The only piece missing now is our Presenter. Presenters must extend the Presenter<TView> base class. Notice how our Presenter takes its dependencies as constructor parameters.

public class AddUserPresenter : Presenter<IAddUserView>
{
private readonly IUserRepository _repository;

public AddUserPresenter(IAddUserView view, IUserRepository repository) : base(view)
{
_repository = repository;
view.AddingUser += OnAddingUser;
}

private void OnAddingUser(object sender, AddUserEventArgs e)
{
var success = _repository.Add(e.User);

if(success)
{
View.Model.Result = FormResult.Success;
View.Model.Message = "User added";
}
else
{
View.Model.Result = FormResult.Error;
View.Model.Message = "Failed to add user";
}
}
}

Notice how practically all logic has moved from the original AddUser control into the AddUserPresenter. All that is left now, is to revisit the AddUser control and plumb in WebFormsMVP. Our control must implement the IAddUserView interface, extend MvpUserControl<TModel> and be bound to a presenter:

[PresenterBinding(typeof (AddUserPresenter))]
public partial class AddUser : MvpUserControl<AddUserViewModel>, IAddUserView
{
public void CreateUser(User user)
{
if (AddingUser != null)
{
AddingUser(this, new AddUserEventArgs {User = user});

//TODO: refactor into own presenter etc
message.AddClass(Model.Result == FormResult.Success ? "msg--success" : "msg--error");
message.InnerText = Model.Message;
}
}

\#region Implementation of IAddUserView

public event EventHandler<AddUserEventArgs> AddingUser;

\#endregion
}

The PresenterBindingAttribute tells WebFormsMVP which presenter is responsible for this View. This is not 100% necessary as WebFormsMVP contains a number of convention-based discovery strategies for Presenters, however I prefer the explicit binding that the attribute affords – so I follow this pattern instead.

Notice that we’re no longer hydrating our model manually, and that the CreateUser method takes a strongly-typed model. So how is this method invoked? It’s not a standard click event as we had previously (no sender or event object), this is actually one side of two-way data-binding . The other side is the use of ObjectDataSource (or more specifically the WebFormsMVP extension, PageDataSource) control. To get the two-way data-binding to we need our form to be wrapped in a data bound control such as FormView:

<p runat="server" id="message" class="msg"></p>

<fieldset>
<legend>User details</legend>

<asp:FormView ID="addUserFormView" runat="server" DefaultMode="Insert" DataSourceID="userSource" RenderOuterTable="False">
<InsertItemTemplate>
<div>
<label for="firstName">First name</label>
<asp:TextBox runat="server" ID="firstName" Text='<%# Bind("FirstName") %>'/>
</div>
<div>
<label for="lastName">Last name</label>
<asp:TextBox runat="server" ID="lastName" Text='<%# Bind("LastName") %>' />
</div>
<div>
<label for="telephoneNumber">Telepone</label>
<asp:TextBox runat="server" ID="telephoneNumber" Text='<%# Bind("TelephoneNumber") %>' />
</div>
<asp:Button ID="Button1" runat="server" CommandName="Insert" Text="Add user"/>
</InsertItemTemplate>
</asp:FormView>
</fieldset>

<mvp:PageDataSource runat="server" ID="userSource"
DataObjectTypeName="WebFormsLove.Core.Models.User"
InsertMethod="CreateUser" />

On the FormView, we set it’s DefaultMode to "Insert" and it’s DataSourceId to the appropriate name. We tell the PageDataSource control which model the form represents and which method to call for insertion, CreateUser. The submit button no longer has it’s own click event, but instead uses the CommandName property with a value of "Insert" so that it’s click can be mapped to the PageDataSource.InsertMethod property. Each textbox’s value is now bound to the appropriate parameters via a <%# Bind(“PropertyName”) %> expression. From this we get strongly typed data in and out of the form, with very little effort.

Whilst our code is now a lot cleaner, and responsibility divided more appropriately, one issue remains. The View is still responsible for reporting success back to the user. If we had a multitude of forms throughout our application, all requiring a consistent approach to reporting back to the user, then currently we would have to duplicate this code in many places. Instead of copy-paste, we can move the reporting into its own Presenter, and take advanatage of the WebFormsMVP message bus for cross-presenter communication. We can then reuse this Presenter throughout our application, increasing code reuse whilst keeping our code highly decoupled.

Again, we need to create a View (and associated interface), a Model and a Presenter. Our View is so simple it doesn’t explicitly need a a new interface, so we can just use the basic IView<TModel> interface directly.

public class FormMessageModel
{
public FormMessageModel()
{
Result = FormResult.None;
}

public FormResult Result { get; set; }
public string Message { get; set; }
}

public class FormMessagePresenter : Presenter<IView<FormMessageModel>>
{
public FormMessagePresenter(IView<FormMessageModel> view) : base(view)
{
view.Load += OnLoad;
}

private void OnLoad(object sender, EventArgs e)
{
Messages.Subscribe<FormMessageModel>(msg => { View.Model = msg; });
}
}

The FormMessagePresenter does very little other than listen for incoming messages on the bus. The subject of messages are object types (rather than string names as you may be used to in, say, a JS framework), and the message dispatcher is aware of type inheritance . If you subscribe to Object messages you will receive all messages sent by everyone, which is unlikely to be intentional! For this reason, messages should ideally be a custom type. Also, messages are always delivered, even if you subscribe after another Presenter has published a message.

Here’s the control for our FormMessagePresenter:

<asp:MultiView runat="server" ID="mvFormMessage">
<asp:View runat="server" ID="successView" >
<div class="msg msg--success">
<p><%= Model.Message %></p>
</div>
</asp:View>
<asp:View runat="server" ID="errorView">
<div class="msg msg--error">
<p><%= Model.Message %></p>
</div>
</asp:View>
<asp:View runat="server" ID="emptyView"/>
</asp:MultiView>

And the code-behind:

[PresenterBinding(typeof(FormMessagePresenter))]
public partial class FormMessage : MvpUserControl<FormMessageModel>, IFormMessageView
{
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);

View view;
switch (Model.Result)
{
case FormResult.Success:
view = successView;
break;
case FormResult.Error:
view = errorView;
break;
default: //FormResult.None
view = emptyView;
break;
}

mvFormMessage.SetActiveView(view);
}
}

Now we have a nicely encapsulated FormMessagePresenter awaiting messages, we can modify our AddUserPresenter to Publish a message after adding a user:

public class AddUserPresenter : Presenter<IAddUserView>
{
private readonly IUserRepository _repository;

public AddUserPresenter(IAddUserView view, IUserRepository repository) : base(view)
{
_repository = repository;
view.AddingUser += OnAddingUser;
}

private void OnAddingUser(object sender, AddUserEventArgs e)
{
var success = _repository.Add(e.User);

var msg = success
? new FormMessageModel {Result = FormResult.Success, Message = "User added" }
: new FormMessageModel {Result = FormResult.Error, Message = "User add failed" };

Messages.Publish(msg);
}
}

And relieve the AddUser control of the responsibility of reporting back to the user. Note: since we have delegated responsibility for reporting back to the user to the FormMessagePresenter, the AddUser View no longer needs a Model.

public interface IAddUserView : IView
{
void CreateUser(User user);

event EventHandler<AddUserEventArgs> AddingUser;
}

[PresenterBinding(typeof (AddUserPresenter))]
public partial class AddUser : MvpUserControl, IAddUserView
{
public AddUser()
{
AutoDataBind = false;
}

public void CreateUser(User user)
{
if (AddingUser != null)
{
AddingUser(this, new AddUserEventArgs {User = user});
}
}

\#region Implementation of IAddUserView

public event EventHandler<AddUserEventArgs> AddingUser;

\#endregion
}

Testing

The whole point of MVP is to encourage testability, yet so far we haven’t seen a single test! Let’s remedy that and check out tests for the AddUserPresenter. We’re assuming usage of RhinoMocks for creating test mocks, but any equivalent library will do. Our tests should prove:

  • The presenter subscribes to all relevant events on the view
  • The presenter can add a user to repository, and reports success
  • The presenter handles failure to add a user to repository, and reports error
[TestClass]
public class AddUserPresenterTest
{
private IUserRepository _repo;
private IAddUserView _view;
private AddUserPresenter _presenter;

[TestInitialize]
public void TestInit()
{
_repo = MockRepository.GenerateMock<IUserRepository>();
_view = MockRepository.GenerateStub<IAddUserView>();
_presenter = new AddUserPresenter(_view, _repo)
{
Messages = MockRepository.GenerateMock<IMessageCoordinator>()
};
}

[TestCleanup]
public void TestCleanup()
{
_repo.VerifyAllExpectations();
_view.VerifyAllExpectations();
_presenter.Messages.VerifyAllExpectations();
}

[TestMethod]
public void ConstructorHooksUpEventHandlers()
{
// Arrange
_view.Expect(x => x.AddingUser += Arg<EventHandler<AddUserEventArgs>>.Is.Anything);

// Act
new AddUserPresenter(_view, _repo);
}

[TestMethod]
public void AddsUserToRepository()
{
// Arrange
var user = new User {Id = Guid.NewGuid()};

_repo.Expect(x => x.Add(user)).Return(true);
_presenter.Messages.Expect(x => x.Publish(Arg<FormMessageModel>.Matches(msg => msg.Result == FormResult.Success)));

// Act
// Remember, view delegaes to presenter.
// Therefore to test a method on the presenter you should raise an event on the view!
_view.Raise(x => x.AddingUser += null, _view, new AddUserEventArgs {User = user});
}

[TestMethod]
public void HandlesFailureToAddUser()
{
// Arrange
var user = new User {Id = Guid.NewGuid()};

_repo.Expect(x => x.Add(user)).Return(false);
_presenter.Messages.Expect(x => x.Publish(Arg<FormMessageModel>.Matches(msg => msg.Result == FormResult.Error)));

// Act
_view.Raise(x => x.AddingUser += null, _view, new AddUserEventArgs {User = user});
}
}

Testing the FormMessagePresenter would be a very similar process, so it’s not worth covering here.

Dependency Injection

Presenters should have all their dependencies passed into the constructor, the WebFormsMVP framework instantiates each Presenter after the view has loaded, so how does it resolve dependencies? The simple answer is: it doesn’t! WebFormsMVP comes with a number of adapters for popular IoC frameworks which it simply asks to resolve dependencies for. Configure all dependencies in your IoC container, as you normally would and then register the relevant WebFormsMVP adapter in your global.asax:

private void Application_Start(object sender, EventArgs e)
{
var unityContainer = ConfigureUnityContainer();
PresenterBinder.Factory = new UnityPresenterFactory(unityContainer);
}

private static UnityContainer ConfigureUnityContainer()
{
var unityContainer = new UnityContainer();
var section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
if (section != null)
{
section.Configure(unityContainer);
}

return unityContainer;
}

Wrapping up WebFormsMVP

Our add user control is now incredibly simple – besides raising events it does very little. All our business logic is encapsulated in the AddUserPresenter and is entirely testable. Reporting success/failure has been refactored into a reusable Presenter/View/Model, FormMessagePresenter. Everything is decoupled by communicating strictly over the WebFormsMVP message bus. And finally, we have everything working with our favourite IoC container to have all dependencies injected into each Presenter.

Abstracting Sitecore’s API

With the MVP pattern we have seen how your controls can be organised to promote testability. However, the scenario we’ve been running through so far has had no interaction with Sitecore’s API. Imagine the form label text was pulled from Sitecore, meaning we have to consume Sitecore.Context.Item. We will either have to put this logic into our View (bad) or Presenter (untestable). How can we remedy this situation?

Let’s first imagine the following code, typical of a Sitecore solution:

Item context = Sitecore.Context.Item;
titleLiteral.Text = FieldRenderer.Render(context, "title");

This short snippet seems fairly innocuous, but it is problematic in many ways: tightly-coupled to concrete implementations, refers to the global state in Sitecore.Context and a magic string referring to a field name.

The naive approach to solving these issues is to manually wrap as much of Sitecore’s API as possible. This sounds reasonable, but you will quickly discover it is not a trivial task, requiring masses of boilerplate code. Perhaps then we should limit scope, focussing on the classes we use most regularly.

public interface IField
{
Field Original { get; }
string Id { get; }
string Name { get; }
string RawValue { get; }

string Render();
}

public interface IItem
{
Item Original { get; }
string Id { get; }

IField GetField(string name);
}

public interface IItemRetriever
{
IItem GetContextItem();

IItem SelectSingleByPath(string path);
IEnumerable<IItem> SelectByPath(string path);
}

Whilst these interfaces only cover a fraction of the Sitecore API’s surface area, they get us most of the way to breaking the hard dependencies on Sitecore types.

Some problems still exist however. We don’t have any strong typing on IItem, the magic strings still persist! Imagine this was an excerpt from a code behind:

var item = _itemRetriever.GetContextItem();
var titleField = item.GetField("title");
titleLiteral.Text = titleField.Render();

Why is it so dangerous to refer to fields by name? Suppose we refactor this Sitecore template, renaming the “title” field to something more appropriate. We would have to update every reference to that field in our code. Some references can easily be overlooked in this situation and this is where danger creeps in. Referring to a non-existent field in Sitecore does not cause a compile-time error, but a run-time error! Of course we want to avoid the possibility of run-time errors.

Strongly-typed Items

Strongly-typing Sitecore items would reduce the occurrence of this class of errors. So how might we achieve something like that? We could create an IItemMapper whose responsibility is to map each field from an item onto some model class.

public interface IItemMapper
{
TModel MapTo<TModel>(Item item) where TModel : IItem
}

Since we’ll now be working with strongly-typed items, we should modify some of our interfaces to allow arbitrary types to be returned:

public interface IItemRetriever
{
TModel GetContextItem<TModel>() where TModel : IItem;

TModel SelectSingleByPath<TModel>(string path) where TModel : IItem;
IEnumerable<TModel> SelectByPath<TModel>(string path) where TModel : IItem;
}

Before we discuss anymore it’s worth considering how a model may look, along with a concrete implementation of IItemRetriever. This will show us how everything is tied together.

public class FieldMapAttribute : Attribute
{
private string _name;

public FieldMapAttribute(string name)
{
_name = name;
}
}

public class MyItem : IItem
{
[FieldMap("title")]
public IField Title { get; set; }

// etc
}

public class ItemRetriever : IItemRetriever
{
private IItemMapper _mapper;

public ItemRetriever(IItemMapper mapper)
{
_mapper = mapper;
}

TModel GetContextItem<TModel>() where TModel : IItem
{
return _mapper.MapTo<TModel>(Sitecore.Context.Item);
}

// etc
}

How does this work? ItemRetriever delegates to IItemMapper. IItemMapper reflects on the type it’s given, searching for any usage of FieldMapAttribute. With a list of field mappings, the mapper can wrap fields in an implementation of IField and return a strongly-typed model ready for use.

var item = _itemRetriever.GetContextItem<MyItem>();
titleLiteral.Text = item.Title.Render();

Our code no longer exhibits any of the issues present in the original snippet: no globals, no magic strings, no concrete implementations!

Model Generation

We’ve discussed how, with a few classes, we can abstract away the vast majority of code consuming the sitecore API. But what about all the models? You may be working on a site with hundreds or thousands of templates, writing classes for every one of these would be tedious and error-prone. Besides, with the process of writing strongly-typed classes being so mechanical, it’s begging for some automation!

Text Template Transformation Toolkit (or T4 for short) is the perfect tool for automating creation of these classes.

Whilst there are a number of projects available for generating strongly-typed items, we chose to use Kern Herskind’s TDS T4 Model Generation project. TDS T4 Model Generation uses Team Development for Sitecore for it’s data store, and as we already use TDS it seemed a perfect fit. TDS T4 Model Generation walks the item tree in TDS, finds all templates and generates classes and interfaces for each of them. It has has a deep collection of field types, so you can work with Reference Fields etc through a clean API. It also ships with a number of classes that mirror the hypothetical interfaces I outlined above. What this all means is that you can generate strongly-typed items directly from within visual studio with tools you’re probably already using anyway!

Combining WebFormsMVP + an abstracted sitecore API

Armed with strongly-typed items, and a wrapper around the most common parts of the Sitecore API, we can now put this kind of code into our Presenter and still write tests for it!

Let’s modify our previous exmaple to pull some data from Sitecore. First we should (re)create the AddUserModel.

public class AddUserModel
{
public IAddUserTemplate Item { get; set; }
}

Then we can modify our Presenter to accept an IItemRetriever as a dependency, and populate the Model:

public class AddUserPresenter : Presenter<IAddUserView>
{
private readonly IUserRepository _repository;
private readonly IItemRetriever _itemRetriever;

public AddUserPresenter(IAddUserView view, IUserRepository repository, IItemRetriever itemRetriever) : base(view)
{
_repository = repository;
_itemRetriever = itemRetriever;

view.OnLoad += OnLoad
view.AddingUser += OnAddingUser;
}

private void OnLoad(object sender, EventArgs e)
{
wiew.Model.Item = _itemRetriever.GetContextItem<IAddUserTemplate>();
}

// etc
}

And in our View we can now output data from the Sitecore item. Note, because this is inside a data bound control (our FormView), we’re using the data bind expression, <%# %>. If this code was not in a data bound control, you would instead use the standard inline expression, <%= %>

<label for="firstName"><%# Model.Item.FirstNameLabel.Render() %></label>

That’s about it. Would be interested in hearing other people’s thoughts.