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:
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);
}
childNode.Tag = NodeType.Child;
childNode.Tag = NodeType.Child;
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);
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));
potentialParents.SingleOrDefault(n => n.Tag.Equals(tag));
}
if
(actualParent != null)
{
var
childNode =
actualParent.Nodes.Add(key:id.ToString(), text:description);
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)
TreeNodeCollection nodes,
string key,
object tag)
{
var
potentialParents =
nodes.Find(key, searchAllChildren: true);
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));
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);
TreeNodeFinder.Find(
TreeView.Nodes,
id.ToString(),
NodeType.Parent);
if
(parent != null)
{
var
childNode =
parent.Nodes.Add(key: id.ToString(), text: description);
parent.Nodes.Add(key: id.ToString(), text: description);
childNode.Tag = NodeType.Child;
}
}
}
No comments:
Post a Comment