Another Look at the Test Container
If you are an aficionado of test-driven design, you know that unit testing of UI components always presents its own set of difficulties. The UserControl Test Container of Visual Studio exemplifies a good intermediate step between automated unit testing and manual UI testing at the application level. The test container is still manual UI testing, but it allows you to test a single unit (control) rather than having to test an application in its entirety. I call isolated unit testing
container testing.
The AlarmClockControl that you built provides sufficient means to exercise the control, via the
AlarmTime property, the
AlarmSet property, and the Disable Alarm button. However, AlarmClockControl is probably in the minority; generally user controls will not provide sufficient means for meaningful testing in isolation. Therefore, you must usually instrument your control for container testing, just as you have to instrument non-UI code for unit testing. For this discussion, take a look at the ProgressBarMessager control running in the test container (see
Figure 9). This control comes from my .NET open source software collection (get the
API, or
download the control collection). The purpose of this control is to enhance a standard ProgressBar to support progress messages that appear sequentially beneath the bar and can provide useful feedback during a lengthy operation. The ProgressBarMessager control also includes a button so users can cancel the operation if desired.
 |
|
Figure 9. The ProgressBarMessager Control: Unlike the AlarmClockControl, this control does not run autonomously; instead, it's instrumented with an extra ContainerTest property for the express purpose of container testing. Enabling that property causes an action that allows the control to be exercised in the test container. |
The ProgressBarMessager has an added property called
ContainerTest that defaults to
false (see
Figure 9). The intent is that changing the
ContainerTest property to
true should reveal additional controls that are necessary and sufficient to enable testing this control in isolation, without having to first embed it in a master form and hook it up manually. In this case, when you set
ContainerTest to
true, the control responds by displaying a hidden Step button. This button performs one step in the natural action of a progress bar. For example, the sequence of frames in
Figure 10 begins at the upper left with the Step button exposed; subsequent frames in the image result from pressing the Step button repeatedly.
 |
|
Figure 10. Testing the ProgressBarMessager: Repeated Step button presses let you test the progress bar, causing additional messages to appear. The control expands vertically up to a preset size, and then scrolls. |
The first press displays an initial message with a small green arrow, indicating it is the current step being executed. That corresponds to a call to
Reset() followed by a call to
ReportInitialMessage(). Subsequent Step presses correspond to calls to the
PerformStep() method. The second press, therefore, changes the arrow in front of the first message to a check and adds an elapsed time for that step. It then adds the second message ("step 2") and an arrow indicating that is now the step being executed. Continued presses of the button add more steps. Note that the control expands vertically to contain a property-selectable number of messages (10 by default). Continued presses beyond that simply scroll the messages, as the final frame in
Figure 10 shows.
When to Test
Testing a control embedded in a complete application or in the UserControl Test Container are both aspects of
run-time testing. But one of the cleverer features of Visual Studio is something you might never notice: the ability to execute your code at
design-time. You may have already noticed this in action when building the examples in this article. For example, the AlarmClockControl showed its parent control (ClockControl) in the designer with a time display that updated every second—
without executing anything! That clock, as you know, gets updated by
your timer tick event handler, so it is clearly running your code.
You aren't limited to passive observation as your code executes; you can interact with the design-time execution of your program by manipulating properties. For example, if you change the AlarmClockControl's
ClockForeColor property in the Properties window of the designer, you are in fact accessing the code that manages that property:
public Color ClockForeColor
{
get { return clockLabel.ForeColor; }
set { clockLabel.ForeColor = value; }
}
Specifically, changing the property invokes the
ClockForeColor setter, which, in this case, executes just one line of code: the assignment to the
ForeColor property of the underlying label. But you are not limited to simple assignments, as evidenced in the aforementioned timer tick event handler.
So what about .NET controls? Placing a Label on your designer surface, and then changing the
BackColor property of that label does the same thing; it executes the setter property of the Label control. In other words, Visual Studio produces the design-time display of a control by executing the code that renders of that control. For further exploration of this topic, see the MSDN topic "
Extending Design-Time Support."