Channels ▼
RSS

Tools

Unit Testing the UI

Source Code Accompanies This Article. Download It Now.


Unit Testing the User Interface

Using MVP and automatic data binding, unit tests for the view become straightforward. Because the view is now responsible for only visualization of values and responding to binding events, you can cover most of the functionality of the view with two unit tests for each display property:

  • TestVisualizingPropertyX
  • TestBindingPropertyX

In order to run a unit test for the view, it is necessary to launch the Form or Control, and inspect its state by inspecting the child controls within the Form or Control. Because child controls within a parent are typically represented by private member variables, you can leverage a Tester class that extracts the controls from the parent. In Listing Four, the GUITester class uses reflection to traverse the parent's hierarchy to retrieve the specified control by the control's Name property.

public static class GUITester
{
    public static T FindControl<T>(Control container, string name)
          where T : Control
    {
        if (container is T && container.Name == name)
            return container as T;
        if (container.Controls.Count == 0)
            return null;
        foreach (Control child in container.Controls)
        {
            Control control = FindControl<T>(child, name);
            if (control != null)
                return control as T;
        }
        return null;
    }
    #endregion
}

Listing Four

Once you can retrieve the child controls, it is straightforward to create a unit test to validate this visualization of a property. All you need to do is launch the form, update the property, and validate the state of the control; see Listing Five.

[TestFixture]
public class CalculatorFormTest
{
    [Test]
    public void TestVisualizingOperand2()
    {
        CalculatorForm target = new CalculatorForm();
        target.Show();
        TextBox operand2TextBox =
           GUITester.FindControl<TextBox>(target, "_operand2TextBox");
        _target.Operand2 = 100;
        Assert.AreEqual(target.Operand2.ToString(),
            _operand2TextBox.Text);
        target.Close();
    }
}

Listing Five

Now that you have test coverage for the visualization, you need to ensure that binding works correctly. This can be achieved with a simple unit test validating that the model and view are kept in sync, as in Listing Six.

 
[TestFixture]
public class CalculatorFormTest
{    
    [Test]
    public void TestBindingOperand2()
    {
        CalculatorForm target = new CalculatorForm();
        Calculator model = target.Calculator();
        target.Show();
        // Update the UI and validate the model
        target.Operand2 = 7;
        Assert.AreEqual(target.Operand2, model.Operand2);
        // Update the model and validate the UI
        model.Operand2 = 9;
        Assert.AreEqual(model.Operand2, target.Operand2);
        target.Close();
    }
}
Listing Six

As long as you create unit tests of this type for each display property, you can add a significant amount of coverage with little unit test code.

Conclusion

By leveraging MVP and automatic data binding, you can create simple unit tests that cover most of the responsibilities of the view. While it is true that some view operations, such as drag-and-drop, are difficult to test, binding and visualization are at the heart of what is happening in a view.

In addition, you can view the wrapper properties used in the example as superfluous. Although it is not necessary to wrap each text box with its own display property, the example serves to demonstrate the simplicity of creating the one-to-one mapping between the presentation property and the view property. How you apply the rule to each new application is up to you.

As long as you adhere to the spirit of MVP, you will find a straightforward approach to unit testing the view.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video