I have read Jeremy’s Build you own CAB series (Build your own CAB Part #2 – The Humble Dialog Box). Jeremy is building the series with the help of d’Artagnan the famous Musketeer wanna-be.
Due to both his pride and an urge to ingratiate himself with those he wished to join, d’Artagnan is challenged to a duel by three musketeers Athos, Porthos, and Aramis. d’Artagnan had a little surprise for his opponents. He did not divide his concerns, instead he embraced them, and found himself fighting all 3 together. The 3 started fighting with each other over who will fight d’Artagnan first, but they all ended up fighting Cardinal Richelieu’s guards, who threaten to arrest them because duels are forbidden by royal decree…
I am going to show you just how we can manage to unit test a Simple Dialog without separating concerns and overcome the forbidden decree that GUI is hard to test.
I will use Jeremy’s requirement:
If the user attempts to close the XYZ screen without saving any outstanding changes, a message box is displayed to warn the user that they are discarding changes. If the user wishes to retain the outstanding work, do not close the screen. The message box should not be shown if the data has not been changed.
private void SimpleForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (isDirty())
{
bool canClose = MessageBox.Show(“Ok to discard changes or cancel to keep working”, “Confirm”,MessageBoxButtons.OKCancel) == DialogResult.OK;
e.Cancel = !canClose;
}
}
Unlike Jeremy, MessageBox will be no problem at all to test, actually the tests are quite straight forward and will give you the developer the freedom to choose (and refactor) the code when needed.
Here are the tests:
[Test]
[VerifyMocks]
public void CloseTheScreenWhenTheScreenIsNotDirty()
{
SimpleForm testedDialog = new SimpleForm();
using (RecordExpectations record = RecorderManager.StartRecording())
{
testedDialog.isDirty(); // mocked
record.Return(false).RepeatAlways();
}
testedDialog.Show();
testedDialog.Close();
Assert.AreEqual(false, testedDialog.Visible);
}
[Test]
[VerifyMocks]
public void CloseTheScreenWhenTheScreenIsDirtyAndTheUserDecidesToDiscardTheChanges()
{
SimpleForm testedDialog = new SimpleForm();
using (RecordExpectations record = RecorderManager.StartRecording())
{
testedDialog.isDirty(); // mocked
record.Return(true).RepeatAlways();
MessageBox.Show(“whatever”); // mocked
record.Return(DialogResult.OK);
}
testedDialog.Show();
testedDialog.Close();
Assert.AreEqual(false, testedDialog.Visible);
}
[Test]
[VerifyMocks]
public void CloseTheScreenWhenTheScreenIsDirtyAndTheUserDecidesNOTToDiscardTheChanges()
{
SimpleForm testedDialog = new SimpleForm();
using (RecordExpectations record = RecorderManager.StartRecording())
{
testedDialog.isDirty(); // mocked
record.Return(true).RepeatAlways();
MessageBox.Show(“whatever”); // mocked
record.Return(DialogResult.Cancel);
}
testedDialog.Show();
testedDialog.Close();
Assert.AreEqual(true, testedDialog.Visible);
}
Cool Ah!, we mocked the unmockable MessageBox, we also mocked the SimpleForm.isDirty, we actually tested the logic that we needed, this makes the GUI very testable with no effort at all.
The mocking is done in the using block where all calls are mocked and the behavior is set. Verification that the expectations where called is done using the [VerifyMocks] attribute. We could also test the message that is being displayed on the message box, but that is another test.
You should of course separate the GUI class when it grows bigger, but your test will still pass even if you do separate the concerns! Unless you change the message box to one of your own, but you have the ability to really do Just In Time Design (YAGNI)
Just one more tip for those of you who will try this technique. It will be a good idea to mock MessageBox.Show in all cases, just in case the test fails-> we don?t want the tests to hang with the message box. Just use .RepeatAll or FailIfCalled to make sure that we don’t fail if the MessageBox is not shown.
Rimsky went look closer buy cytotec then announced estivities.