Model-View-Presenter Seems Easier to Test Than ASP.NET MVC

I have been creating an application using the new ASP.NET MVC Framework in the ASP.NET 3.5 Extensions Preview over the past week and am fumbling with the view-controller testing. I stopped testing the view-controller interaction altogether, because it feels incredibly time consuming and laborious over what I have experienced in the past with Model-View-Presenter.


 


My Experience with Model-View-Presenter


In a fairly recent post, I talked about how I have finally gotten into a groove with View-Presenter Test-Driven Development:


MVP in ASP.NET – Development with Just the Promise of a View


It was an uphill climb, but I feel very, very comfortable with testing the Model-View-Presenter Pattern in ASP.NET. Most of the success has come from doing several projects with the Web Client Software Factory which helps with the View-Presenter Implementation, which you can also learn about in the recently released MVP Bundle.


The beauty about the Model-View-Presenter Pattern is that the Presenter Constructor typically accepts a View Interface, which can easily be mocked by a mocking Tool. In the example I mention in the previous post, View-Presenter Testing is as simple as:


 

[Test]
public void EmptyCustomerNameShouldCauseInvalidCustomerMessage()
{
MockRepository mocks
= new MockRepository();

IAddCustomerView mockView = mocks.CreateMock<IAddCustomerView>();
AddCustomerPresenter presenter
= new AddCustomerPresenter(mockView);

Expect.Call(mockView.CustomerName).Return(string.Empty);
Expect.Call(mockView.CustomerTitle).Return(
Jester);
Expect.Call(mockView.PhoneNumber).Return(
555-1212);
mockView.Message
= Invalid Customer…;

mocks.ReplayAll();
presenter.onAddCustomer();
mocks.VerifyAll();
}


 


The key here is that all I need to do is mock the View Interface, in this case IAddCustomerView. I can then setup expectations on the view and test to make sure those are met. This is incredibly easy after awhile and has become somewhat second nature to me.


 


ASP.NET MVC Framework – View-Controller Testing


Fast forward to this week where I have been all smiles with the simplicity of the ASP.NET MVC Framework and very impressed with its extensibility. Particularly satisfying to me has been how happy I have been with using dependency injection with it using Castle Windsor, Spring.NET, etc. The IControllerFactory in the ASP.NET MVC Framework is really, really nice. I mentioned it here with Castle Windsor and SpringFramework.NET:


SetDefaultControllerFactory and IControllerFactory in ASP.NET MVC Framework


This is cool, because Dependency Injection in webforms has always felt a little kludgy and now it finally feels good in the ASP.NET MVC Framework. 


However, the testability of the View-Controller Interaction seems like a lot of effort. Several people have been pointing to Haack’s post:


Writing Unit Tests For Controller Actions


but the mocking framework versions of his tests are a lot more effort than what you would have to do with MVP. Here is a test to verify a simple redirect:


 

[Test]
public void VerifyAboutRedirectsToCorrectActionUsingMockViewFactory()
{
RouteTable.Routes.Add(
new Route
{
Url
= [controller]/[action],
RouteHandler
= typeof(MvcRouteHandler)
});

HomeController controller = new HomeController();

MockRepository mocks = new MockRepository();
IHttpContext httpContextMock
= mocks.DynamicMock<IHttpContext>();
IHttpRequest requestMock
= mocks.DynamicMock<IHttpRequest>();
IHttpResponse responseMock
= mocks.DynamicMock<IHttpResponse>();
SetupResult.For(httpContextMock.Request).Return(requestMock);
SetupResult.For(httpContextMock.Response).Return(responseMock);
SetupResult.For(requestMock.ApplicationPath).Return(
/);
responseMock.Redirect(
/Home/Index);

RouteData routeData = new RouteData();
routeData.Values.Add(
Action, About);
routeData.Values.Add(
Controller, Home);
ControllerContext contextMock
= new
ControllerContext(httpContextMock, routeData, controller);
mocks.ReplayAll();

controller.ControllerContext = contextMock;
controller.About();

mocks.VerifyAll();
}


 


We end up mocking a lot more than just a View Interface and setting up a lot of extra classes like ControllerContext and RouteData. To be fair, the ASP.NET MVC Framework is still in CTP and we do have much more ability to mock with all the wonderful interfaces, but this just seems like way too much typing over what I am used to with Model-View-Presenter. Haack also shows a Specific SubClass Pattern which does greatly reduce the code, but I still just find the Model-View-Presenter Testing so much easier.


 


Conclusion


My gut here is that unless the View-Controller Testing is made simpler, I am less apt to do it. I miss the simple mocking of the View Interface that I get from Model-View-Presenter and find that although I have a much richer environment for testing, it seems like a lot more work.


If anyone can share some insight, I would greatly appreciate it. I am at a loss for what I am experiencing.


 

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

12 Responses to Model-View-Presenter Seems Easier to Test Than ASP.NET MVC

  1. Joe says:

    @Bill,

    No, it’s not really whether to mock or stub (although I personally prefer mocking). It’s about whether I have to create a Test controller for EACH controller and whether the code is generic. I just find it too much work to do each time.

  2. Bill Pierce says:

    @Joe,

    Your question is Mocking vs. Stubbing? I think that is a personal preference and both have their uses. I like to Stub domain objects for use in multiple test fixtures but I will generally mock service dependencies.

  3. Joe says:

    @Bill,

    Since you have some more experience, can you answer the question I posed to Jeremy?

    Thanks

  4. Bill Pierce says:

    Hey David,
    I didn’t mean to come off so negative. I know you have a large readership and didn’t want them to get the wrong, possibly first, impression of MVC. As I cook-up some real world scenarios, I’ll make sure and pass them along.

    -Bill

  5. David Hayden says:

    Bill,

    I am not trying to say that. I am essentially asking for help.

    I followed it up with another post that hopefully clarifies myself as not being an expert in the matter and that I am just doing a little discovery so I can test my controllers without a lot of work. I believe everyone when they say MVC is easier to test than MVP, but I haven’t found the answer and would love for you to point me to some expert examples.

  6. Bill Pierce says:

    Hey David,
    Your comment to Jeremy: “I realize my other unit test with MVP is trivial, but sometimes you have simple linear tests and I would hate that doing these tests with MVC would be so much more complicated.”

    You make it sound like doing simple linear tests with MVC is much more complicated than with MVP. That is the statement I refute with my comment and post.

    It would also be helpful to your readers if you would write a unit test for doing a redirect in MVP. I feel you are very misleading when you say MVC looks like a lot of work to test and you do not provide an equivalent, simpler, test in MVP.

  7. David Hayden says:

    Bill,

    That wasn’t my intention, so my apologies if that is how it seems.

    I wasn’t comparing the tests in doing the same thing, but the amount of extra mocking with the ASP.NET MVC as opposed to just mocking a view with MVP. I didn’t write that test with ASP.NET MVC. I pulled that directly from Phil Haack’s blog, and just copied and pasted it as an example of what seems like a lot of work.

    However, Jeremy’s example above which is similar to Phil’s example of the Specific SubClass Pattern seems to be a lot less work like I have experienced with MVP and is fine by me. Here are examples similar to Jeffrey’s that I have been testing with and seem fine to me:

    [TestFixture]
    public class CustomersControllerTextFixture
    {
    [Test]
    public void ShouldReturnListOfCustomers()
    {
    MockRepository mocks = new MockRepository();
    ICustomersDataSource mockDataSource = mocks.CreateMock();

    TestCustomersController controller = new TestCustomersController(mockDataSource);

    List customers = new List
    {
    new Customer(),
    new Customer(),
    new Customer()
    };

    Expect.Call(mockDataSource.SelectAll()).Return(customers);

    mocks.ReplayAll();
    controller.Index();
    mocks.VerifyAll();

    Assert.That(controller.ActualViewData, Is.EquivalentTo(customers));
    Assert.IsTrue(controller.ActualViewName.Equals(“Customers”));
    }

    [Test]
    public void SaveActionShouldRedirectToIndexAction()
    {
    MockRepository mocks = new MockRepository();
    ICustomersDataSource mockDataSource = mocks.CreateMock();

    TestCustomersController controller = new TestCustomersController(mockDataSource);

    mocks.ReplayAll();

    controller.Save(“who”, “cares”);

    Assert.AreEqual(controller.RedirectToActionValues, “{ Action = Index }”);
    }

    }

    I am not suggesting that MVP is easier to test than MVC, but that so far it has seemed easier to me. I have a lot more studying to do on this topic :)

  8. Bill Pierce says:

    Hey David,
    You really compared apples to oranges with your two examples. I would like to see you unit test a redirect with your MVP framework.

    Here would be an equivalent MVC unit test of your linear MVP unit test.

    [Test]
    public void EmptyCustomerNameShouldCauseInvalidCustomerMessage()
    {
    CustomerController controller = new CustomerController();

    controller.Add(string.Empty, “Jester”, “555-1212″);

    Assert.IsTrue(controller.ViewData.Containers(“message”));
    Assert.AreEqual(“Invalid Customer…”, controller.ViewData["message"]);
    }

    Which one was simpler again?

    Expect.Call(mockView.CustomerName).Return(string.Empty);
    Expect.Call(mockView.CustomerTitle).Return(“Jester”);
    Expect.Call(mockView.PhoneNumber).Return(“555-1212″);
    mockView.Message = “Invalid Customer…”;

    mocks.ReplayAll();
    presenter.onAddCustomer();
    mocks.VerifyAll();
    }

  9. David Hayden says:

    @Jeremy,

    That unit test is ugly, which is why I avoided it like the plague. I realize my other unit test with MVP is trivial, but sometimes you have simple linear tests and I would hate that doing these tests with MVC would be so much more complicated.

    I have no doubt that testing with ASP.NET MVC is better, but the question is what are the best practices which I can’t seem to find. I would love to see you whip up an example that I can study and learn from :)

    @Jeffery,

    That looks a lot like what Haack referred to as Specific SubClass Pattern, which does greatly reduce the code in the test, but in reality just transfers it to other classes. Is that the best practice with ASP.NET MVC? I am like Joe in that I typically hope RhinoMocks can handle it all without extra classes, but maybe that is not practical in this case.

  10. Joe says:

    Jeremy, in regard to your sample code, is that test controller going to be the same for all controllers sufficiently that I could create a test base controller and inherit from those and make it implement the interfaces of all the different controllers? Otherwise I see it still as too much work, having to create stubs for everything to test it. That’s what the beauty of Rhinomock was all about.

  11. @David,
    I concur with David, and I’ll share my code: http://palermo.googlecode.com/svn/aspnetmvc/trunk

    Here is an excerpt from one of my controller unit tests in ProductControllerTester. Notice there is less setup than yours and there is no need to even mess with a route.

    [Test]
    public void ShouldListProductsInStock()
    {
    MockRepository mocks = new MockRepository();

    IProductListingService service =
    mocks.CreateMock();

    ProductListingItem[] productListingItems = new ProductListingItem[]
    {
    new ProductListingItem(new Product()),
    new ProductListingItem(new Product()),
    new ProductListingItem(new Product())
    };
    SetupResult.For(service.GetProductsInStock()).Return(
    productListingItems);

    mocks.ReplayAll();

    //running code
    TestProductController controller =
    new TestProductController(service, null);
    controller.ListInStock();
    //finished running code.

    Assert.That(controller.ActualViewData,
    Is.EquivalentTo(productListingItems));
    Assert.That(controller.ActualViewName, Is.EqualTo(“List”));
    Assert.That(controller.ActualActionRedirected, Is.Null);
    }

    public class TestProductController : ProductController
    {
    public string ActualActionRedirected;
    public object ActualViewData;
    public string ActualMasterName;
    public string ActualViewName;

    public TestProductController(IProductListingService service,
    IProductRepository repository)
    : base(service, repository)
    {
    }

    protected override void RenderView(string viewName,
    string masterName,
    object viewData)
    {
    ActualViewName = viewName;
    ActualMasterName = masterName;
    ActualViewData = viewData;
    }

    protected override void RedirectToAction(object values)
    {

    // ActualActionRedirected = values.ToString();
    }
    }

    public class FakeView : IView
    {
    public void RenderView(ViewContext viewContext)
    {

    }
    }

  12. David,

    I think the problem is more with your unit test than the framework per se. Use a static stub or a recording stub for all or most of IHttpContext and most of that code in the unit test goes away.

    MVC should allow you to move much more of the Controller to View code into easier state based testing. Stub all the IHttpContext stuff instead of mocking it. Just load up the incoming data, pass it into the Controller, and check the state of the “Model” afterwards. My vote is that state based testing is usually easier than interaction based testing.

    RouteData routeData = new RouteData();
    routeData.Values.Add(“Action”, “About”);
    routeData.Values.Add(“Controller”, “Home”);

    This code is just flat out ugly. Again though, it’s as much an issue with the unit test as the ugly API. I’d create an ObjectMother or something similar for constructing RouteData in a single line of code in the test. I got blistered in my blog last month for suggesting that C# 4 should get a one line syntax for creating hash tables ala Ruby. I’m feeling very justified about that request after looking at that ugliness above that’s gonna get created over and over again.

    I still think the linear MVC approach has more to offer in terms of testability in the end than MVP with WebForms ever can. You can beat the Route handling testability with some better test harness helpers to eliminate the tediousness of setting up the HttpContext stuff. If you make the inevitable comparison to Ruby on Rails, you’ll see that RoR has a lot of Rails specific extensions to the Ruby xUnit equivalent and RSpec to make Rails testing easier for exactly the kind of scenarios that you described. The MVC framework just needs some of the same.

    Your example of MVP testing with WebForms isn’t complex enough to bring out the pain of MVP with WebForms. That sample worked perfectly because it was “linear.” The screen lifetime was easily predictable. As soon as you throw screen behavior happening from different points in the page lifecycle from subsequent Postback’s I think the MVP abstraction leaks badly in ASP.NET. In the MVC lifecycle the lifecycle is *always* linear and predictable in its flow compared to the WebForms event lifecycle.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>