Ang3lFir3 – Life as a Code Poet

January 24, 2011

Refactoring MVP to MVC the slow way : pt1 Extracting Services

Filed under: .NET, ASP.Net MVC, C#, Patterns, technology, Uncategorized, web development — Tags: , , , , , , , , , — ang3lfir3 @ 9:58 pm

In this series I would like to examine the process my team and I have been undertaking on our current project. One of the aspects of working on legacy applications is that when you dive in you often see patterns that are not easily testable. The current application my team is working on was built many years ago and implements MVP (model view presenter) ,which at the time was a useful pattern for developing testable ASP.Net webforms applications. In the years since then many new patterns and frameworks have emerged and it is our desire to move the application from MVP to MVC (model view controller).

This series assumes that you are familiar with a few patterns and concepts namely the following:

  • Dependancy Injection
  • Inversion of Control
  • SOLID principals
  • MVP
  • MVC
  • TDD
  • Mocks/Stubs

The Problem

One thing that often occurs when using MVP is the tendency to potentially ask your presenter to do too much. I also often see over use of static classes in presenters. As many of you know static classes are hard to Mock thus making other testing scenarios difficult, especially when those static classes access data in a db.  Presenter creation does not usually take advantage of the Container and therefore does not allow us to inject mocks when testing.

A typical instantiation of a presenter would be:

public class SamplePage : ISampleView
{
    public override void OnInit(EventArgs e)
    {
         base.OnInit(e);
         presenter = new SamplePresenter(this);
    }
}

Another thing I often see is that it becomes really easy to allow the presenter to do too much. It is a convenient place to sometimes place a lot of logic that should probably be somewhere else and very discreet. In the below example you can see that our presenter takes on some responsibilities that might be better suited to some external service.

A Solution to statics

I say “a solution” because I am certain there are other options, however we will have a look at the one we are using on my team. The first thing we decided to tackle was the static class problem.

A naive example:

public class SamplePresenter
{
    .....

    public void LoadModels()
    {
        view.Models = ModelBuilder.BuildFor(DateTime.Today);
    }
    .....
}

In this case you can see that ModelBuilder is a static class. It happens to access the datastore and create new entities if they don’t already exist. Internally it loads the ModelRepository from the container via service location and uses it. That is great and all but it makes it hard for me to test other parts of the presenter that may need to mock results from the model builder. The simplest solution, was a simple wrapper class that exposes an interface. It is created via service location as well, for now.

public interface IModelConstructor
{
    IList BuildFor(DateTime a_date);
}
public class ModelConstructor : IModelConstructor
{
    public IList BuildFor(DateTime a_date)
	{
	    return ModelBuilder.BuildFor(a_date);
	}
}

Usage in the presenter is now a little bit more complex… but much easier to mock. As you can see we have a helper IoC class that is static. This gives us access to the container. Sometimes things get worse before they get better.

public class SamplePresenter
{
    public SamplePresenter(ISampleView view)
    {
        this.view = view;
        this.modelConstructor = IoC.Resolve();
    }

    public void LoadModels()
    {
        view.Models = modelConstructor.BuildFor(DateTime.Today);
    }
    .....
}

What this does give us is the ability to use a container that contains mock instances in our tests allowing us to stub methods on them as needed. I use MSpec for most of my testing so the tests below are in that format.

public class with_a_container_of_mocks
{
	Establish context = () =>
	{
	     container = new WindsorContainer();
             IoC.Initialize(container);

	     var modelConstructor = MockRepository.GenerateStub();
	     container.Register(Component.For().Instance(modelContructor));
	};
}

Now we can simply access the mock via service location and provide a stub:

public class when_constructing_models: with_a_container_of_mocks
{
	Establish context = () =>
	{
              .....
              var modelConstructor = IoC.Resolve();
              modelConstructor.Stub(x => x.BuildFor(Arg.Is.Equal(Clock.Today)).Return(someFakeModels);
	};
	......
}

Making presenters do less

This example shows a presenter doing a little more than maybe it should be doing.

public class SamplePresenter
{
    ....
    public void CalculateDistributionFees()
    {
	    //naive example of something that might need to go somewhere else
	    return models.SelectMany(model => model.Account.IsFeeBased)
                              .Sum(account => account.DistributionAmount * account.FeePercentage);
    }
    .....
}

It kinda of feels icky to test the presenter when I want to write tests that ensure the calculation of distribution fees is accurate. With all the overhead of mocking up a view etc it can get really noisy trying to test something very simple. But we simply extract a service (I really am trying not to over use that word) and get it tested. Then make use of it via service location in the presenter. This is similar to the previous technique for handling static classes so I will simply show the service being extracted and its corresponding tests. Gunna use a little NBuilder-foo here to make things pretty.

public class when_calculating_distribution_fees_for_fee_based_accounts
{
    Establish context = () =>
    {
	    some_accounts = Builder.CreateListOfSize(5)
                                              .WhereTheFirst(4)
                                                  .Have(x => x.IsFeeBased = true)
                                                  .And(x => x.DistributionAmount = 100.00m)
                                                  .And(x => x.FeePercentage = .10m)
                                               .AndTheNext(1)
                                                  .Have(x => x.IsFeeBased = false)
                                                  .And(x => x.DistributionAmount = 100.00m)
                                                  .And(x => x.FeePercentage = .10m)
                                               .Build();

        distributionFeeCalc = new DistributionFeeCalculator();
    }

	Because of = () =>
	    fees = distributionFeeCalc.CalculateFees(some_accounts);

	It should_only_add_up_the_fee_based_accounts = () =>
	    fee.ShouldEqual(40m);

	protected static IList some_accounts;
	protected static IDistributionFeeCalculator distributionFeeCalc;
	protected static decimal fees;
}

public interface IDistributionFeeCalculator
{
    decimal CalculateFees(IEnumerable accounts);
}

public class DistributionFeeCalculator
{
	public decimal CalculateFees(IEnumerable accounts)
    {
        return accounts.Where(x => x.IsFeeBased).Sum(account => account.DistributionAmount * account.FeePercentage);
    }
}

Whats next

In the next part(s) of the series I will go more into detail about converting presenters into controllers and refactoring views to be simpler. We will see how service location has become, to quote a friend, our “gateway drug to full blown Dependency Injection”. We will also see our tests become simpler and more controllable.

Thanks for reading this far, hoping that you did indeed get this far, and I hope this has either helped some folks out or at least given people some ideas.

please leave comments, ideas, corrections, and hate mail in the comments section below.

About these ads

3 Comments »

  1. [...] This post was mentioned on Twitter by Eric Ridgeway, Eric Ridgeway. Eric Ridgeway said: [Blogged] : Refactoring MVP to MVC the slow way http://is.gd/9bTndf … been meaning to finish this one. nuthin special but its done now. [...]

    Pingback by Tweets that mention Refactoring MVP to MVC the slow way : pt1 Extracting Services « Ang3lFir3 – Life as a Code Poet -- Topsy.com — January 25, 2011 @ 9:03 am

  2. Your post has been extremely useful. Are you planning to publish remaining parts on Refactoring MVP to MVC

    Comment by Roy — February 7, 2014 @ 12:45 pm

  3. Can you please post the next article

    Comment by Pritam Jadhav — August 4, 2014 @ 2:36 am


RSS feed for comments on this post. TrackBack URI

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

The Silver is the New Black Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: