Note: The code has been updated. You should download the Version 2 code here. Links on this website have been changed to point to the new code.
CabSample_v2.zip
Note: I’ve included the complete source code in the zip file attached to this post. It is a complete, working CAB application built with the SCSF. Full source for the DialogBox class and the PopupBox class are included. The unit tests are written with NUnit 2.2.8.
There are two modules: (A) MessageBoxModule and (B) PopupBoxModule.
The MessageBoxModule makes use of a standard MessageBox for one feature and a standard Windows Form dialog for another feature. When you run the unit tests you’ll be interrupted by MessageBox and DialogBox popups.
The PopupBoxModule makes use of my custom PopupBox class (which mimics a MessageBox) and a custom DialogBox class for CAB. When you run the unit tests for this module no popup windows will appear, yet the tests will pass and the data will be correct.
The MessageBox can be a handy little device in a Windows Forms application. It’s cousin, the Dialog box, can be even more handy. There are a lot of occassions when you need to prompt the user for a yes/no type of answer, or notify them of some important event, or get some trivial piece of data from them before proceeding.
Unfortunately, the MessageBox and Dialog box are nightmares when it comes to unit testing. You don’t want to trigger a MessageBox popup when you’re trying to run automated unit tests against your Presenters, or you end up with this situation:
This problem only becomes compounded when you consider that in the Component UI Application Block you typically use a WindowWorkspace to show Dialogs. This is a much different way for showing popup windows than the typical Windows Form methodology which involves creating a Form control and calling ShowDialog() on it. The WindowWorkspace doesn’t return a DialogResult, for one thing. For another, you’re using a UserControl instead of a full-fledged Form control.
So how can we get the functionality that we’re used to using with classic Windows Forms technology and still utilize the CAB framework, all the while appeasing our desire to utilize Dependency Injection so that we can easily mock objects and input for use in automated testing?
I’ll show you my solution.
Injecting A DialogBox
The first thing to understand is what we’re trying to accomplish: We want to be able to inject a MessageBox into a Presenter or other WorkItem. The reason we want to inject the MessageBox is twofold: (1) so we can control the input and output of the MessageBox object in a unit testing scenario and (2) so we can prevent it from being displayed when the calling code executes. Preventing the MessageBox/DialogBox from appearing when it is called is the key to making things work in a unit testing environment like NUnit.
There’s one other glaring problem when attempting to emulate a MessageBox/DialogBox in CAB: There is no DialogResult when you use a WindowWorkspace.
Making the MessageBox or DialogBox close is easy enough if you’re using the Smart Client Software Factory; you can just call Presenter.OnViewClose() and the framework will handle the rest. The WindowWorkspace will close itself and the SmartPart. But that actually causes the Workspace to attempt to Dispose() of the SmartPart, which is not what you want in a Dialog scenario. What you really want to accomplish is a two-step process: First verify that the user has selected a specific button (OK/Cancel or the like) which is typically done with the DialogResult, and then fetch output off the Dialog object. To do that you need a Dialog that hangs around after the WindowWorkspace has closed itself. This means hiding the MessageBox or DialogBox instead of having the Workspace close it.
But how do we have a Dialog tell the Workspace to hide it? That’s where our DialogBox base class comes in.
The DialogBox Base Class
The first part of the solution is the DialogBox base class. It is an abstract class derived from a UserControl and implements an ISmartPartInfoProvider interface. This base class is responsible for the difficult part of this solution: handling button clicking and setting a DialogResult.
Normally, when you create a Dialog object in .NET you use as a Windows Form, create your buttons and set the DialogResult property on those buttons. Then, when a user clicks on one of those buttons the DialogResult for the Form gets set.
Our base class, DialogBox, does the same thing when you make a call to the InitializeClickHandlers(). It loops through the Controls of the subclass and looks for Button objects. When it finds one, it wires up an EventHandler to that Button’s Click() event. This is what the DialogBox base class will use to help send a message to the WindowWorkspace that a Dialog button has been clicked and the WindowWorkspace needs to Hide() the Dialog UserControl.
The WindowWorkspace
The next step is to augment the WindowWorkspace. The Smart Client Software Factory provides a subclassed version of the standard WindowWorkspace which has a couple enhancements to it. We’re going to add some code to the Show() method and include one additional method:
code in Show():
if (smartPart is DialogBox)
{
DialogBox box = (DialogBox)smartPart;
box.OnDialogButtonClick
+= new DialogBox.DialogClose(DialogClose);
box.InitDialog();
}
add this method:
private void DialogClose(Control c)
{
OnHide(c);
}
The Subclassed DialogBox
Once we’ve altered the WindowWorkspace and built our base DialogBox class, it’s time to actually use it. We start by creating a new UserControl and having it subclass from DialogBox instead of UserControl. Add buttons and controls, making sure each button has a DialogResult property set.

In the code for the dialog we have to make a couple alterations:
[SmartPart]
public partial class StringFetchDialog : DialogBox
{
public StringFetchDialog()
{
InitializeComponent();
InitializeClickHandlers(this);
}
public string String
{
get { return _stringTextBox.Text; }
set { _stringTextBox.Text = value; }
}
public override void InitDialog()
{
base.InitDialog();
_stringTextBox.Text = string.Empty;
}
}
Notice the bolded lines of text. InitializeClickHandlers is a base class method that is used to wire-up the Click event for any button on your Dialog. This saves you from having to perform the DialogResult logic on every Dialog you make. The other thing we’ve done is override the DialogBox’s InitDialog() method. This method gets called by our altered WindowWorkspace when it shows a Dialog. You should perform any initializating on your Dialog in this method. In this case we’re setting the TextBox value to String.Empty so that each subsequent time the dialog is shown you do not have the previous text still in the field.
Now our dialog is ready to use.
Presenter Injection
Typically you’ll have some Presenter object or WorkItem that needs to interact with the user in some modal way. Your DialogBox is a dependency that your Presenter is relying on, so you need to provide your Presenter with a DialogBox either through constructor-based injection or property-based injection. Here is a snapshot of the Presenter code:
private IStringService _service;
private PopupBox _popupBox;
private StringFetchDialog _dialog;
[InjectionConstructor]
public PopupBoxViewPresenter(
[ServiceDependency] IStringService service,
[Dependency(Name = SmartPartNames.PopupBox)] PopupBox popupBox,
[Dependency(Name = SmartPartNames.StringFetchDialog)]
StringFetchDialog dialog)
{
_service = service;
_popupBox = popupBox;
_dialog = dialog;
}
Take note of the use of the [Dependency] attribute here with a strong name on the StringFetchDialog. By default, ObjectBuilder will look for a StringFetchDialog with the same name in the hierarchy of WorkItems. If it cannot find one it will create a new one, much as if you had specified:
[Dependency(NotPresentBehavior = NotPresentBehavior.CreateNew)].
This behavior works to our advantage.
In our unit testing class we will build a new StringFetchDialog and add it to our TestableRootWorkItem prior to adding the Presenter under test. This will cause the Presenter to get a reference to our prebuilt StringFetchDialog which we can manipulate for testing purposes. However, in the real WorkItem we will not create a StringFetchDialog. Instead, we’ll let the ObjectBuilder do it for us when it can’t find it.
The Unit Test SetUp
[SetUp]
public void SetUp()
{
_workItem = new TestableRootWorkItem();
_dialog = _workItem.SmartParts.AddNew<StringFetchDialog>
(SmartPartNames.StringFetchDialog);
This is the beginning of our SetUp method in our TestFixture. Notice how we’re adding a new StringFetchDialog and maintaining a reference to it locally in the TestFixture. This allows us to do the following in a test:
[Test]
public void AddString_UserSelectsOK_AddsStringToView()
{
_dialog.String = "Enchant";
_dialog.DialogResult = DialogResult.OK;
_presenter.AddString();
Assert.AreEqual(1, _view.AddNameValues.Count);
Assert.AreEqual("Enchant", _view.AddNameValues[0]);
}
Here we set the expected DialogResult and data properties. Our presenter has been injected with this dialog so whatever values we set it will make use of. This allows us to control the state of the DialogBox for testing. The question you may be asking now is, “How do you prevent it from being shown during a unit test?”
Preventing Dialog Display During Unit Testing
There’s two parts to this solution. The first part is to create a MockWorkspace that does nothing when Show() is called. We can do this by implementing the IWorkspace interface.
class MockWorkspace : IWorkspace
{
public void Show(object smartPart)
{
}
public void Show(object smartPart, ISmartPartInfo smartPartInfo)
{
}
}
The second part is to use this Workspace instead of a real Workspace in our TestableRootWorkItem:
[SetUp]
public void SetUp()
{
_workItem = new TestableRootWorkItem();
_workItem.Workspaces.AddNew<MockWorkspace>
(WorkspaceNames.PopupBoxWorkspace);
_workItem.Workspaces.AddNew<MockWorkspace>
(WorkspaceNames.DialogBoxWorkspace);
Now when we run our unit tests the StringFetchDialog will not popup on the screen. This is the behavior we’re after.
Using The Dialog In A Presenter
So we have a DialogBox that works with CAB and doesn’t popup when we unit test. How do we actually call this code?
First, I’ll show you the Windows Forms way, which we’re all pretty familiar with:
StringFetchDialog dialog = new StringFetchDialog();
if(dialog.ShowDialog() == DialogResult.OK)
{
_service.AddString(dialog.String);
}
The new way isn’t much different:
WorkItem.Workspaces[WorkspaceNames.DialogBoxWorkspace].Show(_dialog);
if (_dialog.DialogResult == DialogResult.OK)
{
_service.AddString(_dialog.String);
}
The main difference is that we don’t have to new-up a Dialog each time we want to use it. Instead, our Presenter has been injected with a StringFetchDialog already, and we juse reuse it each time we need to query the user. Instead of making a call to ShowDialog() we ask the Workspace to show it.
The PopupBox
The other thing I’ve written is the PopupBox class, which is a special implementation of the DialogBox base class designed to mimic the behavior and usage of the standard MessageBox class. The main difference between it and a MessageBox is that the PopupBox is not static. Like the StringFetchDialog it is an instantiated class that is injected into the Presenter. Since it’s a fairly common control it may be best to add it at the Shell level, so all WorkItems have access to it.
MessageBox Way
In the sample project I use a MessageBox to query the user to determine if they really want to change the background color of the form. The calling code looks like so:
DialogResult result =
MessageBox.Show(
"Do you want to change the background color of this form?",
"MessageBox",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
View.UpdateBackgroundColor(true);
else
View.UpdateBackgroundColor(false);
The CAB-friendly version looks very simliar:
_popupBox.Initialize(
"Do you want to change the background color of this form?",
"Change Background Color",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
WorkItem.Workspaces[WorkspaceNames.PopupBoxWorkspace]
.Show(_popupBox);
if (_popupBox.DialogResult == DialogResult.Yes)
View.UpdateBackgroundColor(true);
else
View.UpdateBackgroundColor(false);
Instead of making a call to MessageBox.Show() you make a call to PopupBox.Initialize(), passing in the parameters you want to format the box correctly.
Note: The MessageBox class has several overrides and many parameters you can pass to customize the box when showing it. I’ve only provided overloads for the four most common parameters: text, caption, buttons and icon.
These two classes are contained in the Infrastructure.Library project of the CabSample application attached to this post. To use these objects in your own project you can just copy and paste them. You’ll also need to adjust the WindowWorkspace accordingly, or copy the class from my file.
With these two objects in hand, the PopupBox and DialogBox, you can get around one of the major hurdles in unit testing CAB applications. Prior to having these objects I would have to skip any subroutine that used a MessageBox or DialogBox. That’s disappointing, because I like to have 100% code coverage in the classes I’m testing. I especially like to be able to simulate user input for Dialog situations. Now I can do that.
Enjoy.
CabSample_v2.zip