Thursday, 10 December 2015

Unit Testing Legacy Code - Examples

Last time I wrote about the difficulties I've encountered unit testing legacy code. Today I'm going to show you a simple example that I used in a presentation at work a while ago. I've changed some of the class names but the general gist is the same.

I used a very simple MVC controller, with just a single action. I chose this controller on purpose due to its simplicity as it allowed me to communicate the core ideas very easily. I'll write about a more complex case at some point in the future.

I started with the following code:
public class ReferralController : BaseController
{
 public ActionResult Referral()
 {
  var viewModel = new ReferralViewModel();
  var entity = StaticEntityProvider.Get();

  viewModel.Reasons = entity.Reasons;

  return View(viewModel);
 }
}
Briefly this action creates a view model, gets an entity using a static method, sets a property on the view model using the retrieved entity, and finally returns the view with the view model.

One immediate problem stopping us from unit testing this method is the use of the static Get() method. A second problem that isn't apparent from this code is in the instantiation of the view model. I am of the firm opinion that view models should contain no behaviour or dependencies whatsoever. They should just contain simple properties with getters and setters. This is because their job is to hold data that can be bound to a UI (in this case an MVC view). That is their responsibility. Anything in addition to that violates the single responsibility principle. However in this application view models routinely contained logic, and for this initial refactoring I wasn't prepared to tackle that just yet.

So my refactoring focused on encapsulating the creation of the view model, and on eliminating the static method call. For the view model creation I created a factory interface as shown below:
public interface IViewModelFactory
{
 TModel Create<TModel>() where TModel : BaseViewModel, new();
}
I used a couple generic constraints because the view models all inherited from a base class and they all had a parameterless constructor. The interface has a very simple implementation:
public class ViewModelFactory : IViewModelFactory
{
 public TModel Create<TModel>() where TModel : BaseViewModel, new()
 {
  return new TModel();
 }
}
I then created a wrapper interface and class for the entity provider:
public interface IEntityProviderWrapper
{
 Entity Get();
}

public class EntityProviderWrapper : IEntityProviderWrapper
{
 public Entity Get()
 {
  return StaticEntityProvider.Get();
 }
}
Finally these interfaces can be injected into the controller as follows:
public class ReferralController : BaseController
{
 private readonly IViewModelFactory _viewModelFactory;
 private readonly IEntityProviderWrapper _entityProvider;

 public QuoteReferralController(IViewModelFactory viewModelFactory, IEntityProviderWrapper entityProvider)
 {
  _viewModelFactory = viewModelFactory;
  _entityProvider = entityProvider;
 }

 public ActionResult QuoteReferral()
 {
  var viewModel = _viewModelFactory.Create<ReferralViewModel>();
  var entity = _entityProvider.Get();

  viewModel.Reasons = entity.Reasons;

  return View(viewModel);
 }
}
I admit that this is an extremely simple example, but these ideas can be extended to much more complicated cases. Anyway we now have a controller for which unit tests can now be written as its dependencies have been isolated and fake implementations can be injected into the controller from the test class. Mission accomplished!

No comments:

Post a Comment