Friday 7 November 2014

Uneasy Modelling

Essentially all models are wrong, but some are useful’ – George Box

I heard this quote during a webinar on modelling cycling physiology and its relationship with power output. While the quote is related to statistical modelling, it immediately made me think about the process of developing programming object models.

Sometimes modelling is hard. I often sit with an uneasy feeling that the model I have just isn’t right. Other times, modelling is easy. The right model just seems to fall out. Most times, it’s somewhere in between the two: an initial model is developed with areas that give rise to that feeling of unease. However, with a little patience and some test-driving, the creases can be ironed out and a neat solution emerges.

To help with that uneasy feeling, I now fall-back on this quote and remind myself that a model doesn’t need to be perfect to be useful and reliable. Furthermore, it reminds me that when I might rest on my laurels, complacently thinking that a model is perfect, there might be something that I’ve missed or that can be improved.

Tuesday 4 November 2014

Improving the testability of complex MVP view implementations

When implementing a view in a separated presentation design pattern, there’s value in keeping the implementation as simple as possible. Speaking from a Windows Forms perspective, concrete views are hard to test in an automated way. The testing of how the actual view performs is often performed manually. Therefore, keeping the code in the concrete view as simple as possible and leaving the more complex logic with the presenter, which can be covered with automated tests, appears to be a sensible approach.

For example, I recently developed a presenter that needed to create a visual representation of a hierarchy. While I was sure that my concrete view would use a TreeView, I like to keep my view interfaces abstract and ‘agnostic’ of the actual controls that I might use. Therefore, a simplistic version of my view interface looks like this:

interface INavigationView
{
   void AddParent(int id, string description);
   void AddChild(int id, string description, int parentId);
}


This view should be abstract enough that it can be implemented using any type of control or combination of controls.

The implementation of AddParent() is simple enough:

public void AddParent(int id, string description)
{
   var rootNode = TreeView.Nodes[RootNodeName];

   var parentNode = 
      rootNode.Nodes.Add(key: id.ToString(), text: description);

   parentNode.Tag = NodeType.Parent;
}

Simple enough, I’d argue, to not warrant automated test coverage. If this code doesn’t work, it’s going to be obvious to the end user pretty quickly. However, when we get to the implementation of the AddChild() method, it gets a little more complex:

public void AddChild(int id, string description, int parentId)
{
   var potentialParents = nodes.Find(key, searchAllChildren:true);

   var potentialParentsCount = potentialParents.Count();

   TreeNode actualParent = null;

   if (potentialParentsCount == 1)
   {
      actualParent = potentialParents[0];
   }
   else if (potentialParentsCount > 1)
   {
      actualParent = 
         potentialParents.SingleOrDefault(n => n.Tag.Equals(tag));
   }

   if (actualParent != null)
   {
      var childNode = 
        actualParent.Nodes.Add(key:id.ToString(), text:description);

       childNode.Tag = NodeType.Child;
   }
}

There’s more complex logic here that I'm uncomfortable leaving to a manual test. Furthermore, the logic is very much related to the Windows Forms TreeView control, so it’s not logic I can promote to the presenter and test there. So, assuming that I cannot cover this view implementation with automated tests, I can re-factor this code into its own class, making it easier to test:

class TreeNodeFinder
{
   public static TreeNode Find(
      TreeNodeCollection nodes, 
      string key, 
      object tag)
   {
      var potentialParents = 
         nodes.Find(key, searchAllChildren: true);

      var potentialParentsCount = potentialParents.Count();

      TreeNode actualParent = null;

      if (potentialParentsCount == 1) 
      {
         actualParent = potentialParents[0];
      }
      else if (potentialParentsCount > 1) 
      {
         actualParent = 
           potentialParents.SingleOrDefault(n => n.Tag.Equals(tag));
      }

      return actualParent;
   }
}

We now have a class that we can comfortably cover with automated tests. We can then integrate this class into our view, reducing the amount of code not covered by automated tests and, arguably, making the intent of the code clearer:


public void AddChild(int id, string description, int parentId)
{
   var parent = 
      TreeNodeFinder.Find(
         TreeView.Nodes, 
         id.ToString(), 
         NodeType.Parent);

   if (parent != null)
   {
      var childNode = 
         parent.Nodes.Add(key: id.ToString(), text: description);
      
      childNode.Tag = NodeType.Child;
   }
}