Yesterday I was engaged by a Test-Driven Development hardliner; a letter of the law type guy. He was very adamant that the .NET Framework and Visual Studio were grossly lacking because there is no way to test the UI, and Test-Driven Development requires a test to be written before any development can occur. The scenario given was that you have a form with a button and a label. When the button gets clicked, the label's text is set to "Hello World". He argued that evaluating whether the label's value was set to "Hello World" is not possible with the current testing framework in Visual Studio. At the time, I agreed with him; not about the severity of the issue, but that it wasn't possible. I am now retracting that assertion.
I thought about that scenario quite a bit since yesterday, and how it might be achieved. First, I thought I might extend the base test type using the Visual Studio SDK (http://msdn2.microsoft.com/en-us/library/bb187323(VS.80).aspx) and provide a wizard which would generate code to use reflection to test the UI. Then, I thought maybe I could just write a whole bunch of code to reference in the tests; a UI Test Framework of sorts. I finally decided before I embarked down either path, I should open Visual Studio to, um, test out the basic assertion and mitigate the possibility that the reflection part might not be possible.
First, I created a Windows Forms application with a button and a label and added a button click event handler which sets the label's text to "Hello World". Next, I went to the form's code view, right-clicked on the empty constructor, and choose "Create Unit Tests…." When the selection window opened up, I verified that the constructor and Initialize methods were selected, chose "OK".
After Visual Studio created the test project and opened the test code file, I realized that I had missed the forest for the trees. The solution was much simpler than I had realized. In fact, the solution had already been built for me, and I wouldn't have to do any more work than I would for a normal unit test. See, behind the scenes, Visual Studio had detected that I was trying to create unit tests for a Windows Forms application, and had generated some extra code to assist.
[TestMethod()]
[DeploymentItem("WindowsFormsApplication1.exe")]
public
void InitializeComponentTest()
{
Form1_Accessor target = new
Form1_Accessor(); // TODO: Initialize to an appropriate value
target.InitializeComponent();
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
The extra code, in part, is the Form1_Accessor symbol. Being the curious type, I checked in the bin directory for the test project, and found two new assemblies: WindowsFormsApplication1_Accessor.dll and WindowsFormsApplication1_Accessor.exe. Using Reflector, I ascertained that Microsoft had indeed built in the functionality and they had used Reflection as I had originally planned.
So rather than writing hundreds of lines of code to add a feature Microsoft already had, I was able to get away with six lines of unit testing code to test the correct behavior of the button click:
[TestMethod()]
[DeploymentItem("WindowsFormsApplication1.exe")]
public
void ButtonClickTest()
{
Form1_Accessor target = new
Form1_Accessor();
target.InitializeComponent();
Assert.AreNotEqual(target.label1.Text, "Hello World", "Pre-click label text is not expected value.");
target.button1_Click(null, null);
Assert.AreEqual(target.label1.Text, "Hello World", "Post-click label text is not expected value.");
target.Dispose(true);
}
Now all you Test-Driven Development guys can test happy!