Geeks With Blogs
Aaron Feng Agile Software Development (XP), Test Driven Development, .NET, etc.....

For anyone doing Test Driven Development, mocks (stubs) are commonly used.  Whether you hand roll your own mocks, or use a mock framework like Rhino Mocks, stubs are used to in order to isolate the code we want to test.  Before we can isolate our code, proper separations of concerns are required; such as implementing the Model View Controller or Model View Presenter pattern for UI testing.  After you practice TDD for a few months, and write countless tests, you start to see a pattern of how to make your presentation layer testable and everything becomes automatic and second nature.

One of the big benefits of TDD is using tests to guide your design.  Have you ever stopped and wondered if your design has really improved by structuring your code in a manner that is testable?  Are you creating more duplication or allowing too much accessibility?  Let’s examine how the presentation layer can be tested using the MVP pattern.  I will use a simple example where the customer information can enter entered.  I purposely omitted the presenter interaction with the view in order to put more focus on the view.

public interface ICustomerDetailsView {

  string CustomerFirstName { get; }

  string CustomerLastName { get;}

  List<Order> Orders { get; }

}

 

public class CustomDetailsView : UserControl, ICustomerDetailsView { 

  public string CustomerFirstName {

    get { return this.firstNameTextBox.Text; }

  }

 

  public string CustomerLastName {

    get { return this.lastNameTextBox.Text; }

  }

 

  public List<Order> Orders {

    get {

      List<Order> orders = new List<Order>();

      foreach (Order order in this.ordersComboBox.Items) {

        orders.Add(order);

      }

      return orders;

    }

  }

}

 

We start with an interface called ICustomerDetailsView to describe behaviors of the view.  Now we can use the interface to create our mock view.  In the MVP pattern, the presenter is an extension to the view, so all the interactions occuring in the view will be delegated to the presenter.  With the delgation to the presenter, we would only have to explicitly test the presenter.  The reason the view is not tested is because all the logic lives inside of the presenter, so we end up testing the API.

 

public class MockCustomDetailsView : ICustomerDetailsView {

  // implement ICustomerDetailsView

  // you can have setters here so values can be injected for testing

}

The presenter can now use the mock view during automated testing and will not know a difference from the real thing.  All your tests pass and life is good.  Before you checkin your code, you decide to smoke your application.  To your surprise, your view does not behave correctly.  You poke around for a few seconds and you remember your tests actually use the mock view, so you never wired up the real thing.  In this simple scenario, when all your tests pass it does not mean your application will work correctly if you never wire it up.  Sometimes it can feel a little awkward when you are writing the mock view to mimic the real view, so your tests can pass.  On top of that, the interface was only added to make the mock possible in this scenario.  I understand creating an interface is considered “best practice” to loosen up the coupling, but in this case, the interface was created to make stubbing possible.  It is a little smelly to me because files were added only to make testing possible, and the real view was treated like a second class citizen.  Maybe this is not so bad, and I should not lose sleep over it.

Maybe life would be a whole lot better if we could use the real view in this case for testing.  That would eliminate our problem of forgetting to wire things up.  It would also reduce the number of files we have to write, and would achieve the same outcome.  Let’s see if we can create another implementation of CustomerDetailsView to accomplish this.  We will call the new view ICustomerDetailsView2:

public class CustomDetailsView2 : UserControl {

  public string CustomerFirstName {

    get { return this.firstNameTextBox.Text; }

    set { this.firstNameTextBox.Text = value; }

  }

 

  public string CustomerLastName {

    get { return this.lastNameTextBox.Text; }

    set { this.lastNameTextBox.Text = value; }

  }

 

  public List<Order> Orders {

    get {

      List<Order> orders = new List<Order>();

      foreach (Order order in this.ordersComboBox.Items) {

        orders.Add(order);

      }

      return orders;

    }

 

    set {

      foreach (Order order in value) {

        this.ordersComboBox.Items.Add(order);

      }

    }

  }

}

We can use this view for both testing and production code, but the side effect is that we have to expose setters only for testing purposes.  In our first implementation, we were able to put the setters only on the mock, so our real view is not polluted.  The reason the setter is unnecessary for CustomerDetailsView2 is because we would never programmatically call the setter.  The setter is used to simulate data input from the user.

 

Either way  you look at it, there are some drawbacks to both techniques.  This really depends on what you consider the lesser of the evils.  I know there are ways to solve this problem by using a mock framework, but then again are you adding technology in order to make testing possible?  Let’s say we live in a world where we do not need to do any testing.  Whatever code we write will always work.  Would you have implemented CustomerDetailsView the same way?  The second example exposes setters when the real system will never use it.  Is that breaking the accessibility of the view?

 

I understand the ability to mock things out is good because that means your system has enough abstractions.  I am just not sure mocks should be used every where in order to make testing possible.  I know there is a fine line here, like many good things in the Computer Science world, but I often struggle to choose the optimal way.  I hope no one got the impression that I am bashing on TDD or mock objects. In fact, it is quiet the opposite.

Posted on Wednesday, July 5, 2006 8:52 PM TDD | Back to top


Comments on this post: Presentation Layer Testing Thoughts

# re: Presentation Layer Testing Thoughts
Requesting Gravatar...
I am wondering if your problem here is this statement: "With the delgation to the presenter, we would only have to explicitly test the presenter." I think this statement could be the cause of your problem when you get to wiring up the real thing and your smoke test fails.

This view is an extremely oversimplified example, but even so it has one somewhat complicated property get in "public List<Order> Orders;" This simple property alone could cause bugs if the presenter mistakingly thought that by manipulating this list that they were manipulating the view's list. If the mock's property isn't implemented exactly the same, then the presenter's tests could pass. But then, if the mock is implemented exactly the same then you have duplicate code and if one changes you have to be sure to change the other... gets complicated real quick.

I just typed a whole lot but I'm not sure if I really added any value to this post, lol...
Left by Todd on Jul 07, 2006 11:38 AM

Your comment:
 (will show your gravatar)


Copyright © Aaron Feng | Powered by: GeeksWithBlogs.net