Automating windows forms UI testing

Posted on November 6, 2008

0


If you have been blessed with a windows-forms application that has business-logic invading the user-interface, I have something that’ll help you. The most practical approach in this situation is to rearchitect the business layer and switch the UI over in a piecemeal fashion. While you’re doing this, automated testing of the user-interface will make your job a lot faster compared to manual testing.

For the purpose of this article let us consider a simple Windows Forms application (download link below). The application allows the user to provide two inputs and then either add them or subtract them. Simple, right?

The application
screenshot

Here’s the code for the event-handlers of interest to us:

private void MainForm_Load(object sender, EventArgs e)
{
this.lblAnswer.Text = “0″;
}

private void btnAdd_Click(object sender, EventArgs e)
{
this.lblAnswer.Text = Convert.ToString(this.numericUpDown1.Value + this.numericUpDown2.Value);
}

private void btnSubtract_Click(object sender, EventArgs e)
{
this.lblAnswer.Text = Convert.ToString(this.numericUpDown1.Value – this.numericUpDown2.Value);
}

The handler for the load event of the form just sets the answer label to display “0”. The handler for the click event of each of the buttons retrieves the selected values from the numeric drop-downs, applies the appropriate operation, and writes the result into the label.

The tests

In order to write tests for this application, you can start by right-clicking on the class of the form (in this case MainForm), and selecting “Create Unit Tests…”. Hit “OK” on the dialog that pops up to create unit-tests in a separate project. Another dialog will pop up asking for the name of the test project. Just hit “Create”.

This will cause Visual Studio to do some heavy lifting for us and generate accessors to allow us to call methods and properties defined on MainForm, from our tests. We don’t have to write code for accessors using the Reflection API ourselves – so this is extremely cool. Doing it on our own is quite painful (proof).

Delete all the tests marked with the TestMethod attribute in the generated class because we’re going to write our own.

The first one is pretty simple. We expect the answer label to display a “0” when the form is initially loaded – so let’s test that.

///

///A test for MainForm_Load
///

[TestMethod()]
[DeploymentItem("AppUnderTest1.exe")]
public void MainForm_LoadTest()
{
MainForm_Accessor target = new MainForm_Accessor();
target.MainForm_Load(null, null);
Assert.AreEqual(“0″, target.lblAnswer.Text, “Initial answer is zero”);
}

Notice that we can call MainForm_Load directly (no ConstructorInfo, MethodInfo – reflection stuff required).

Once that’s done we can just check the text displayed in the answer label and make sure it is “0”.

We can take the same approach to call the click event handlers of the buttons and check the results for addition/subtraction.

Since there are various cases to test, I have a separate function GetTestTable that returns a two dimensional array where each row contains the two test-inputs, result of subtraction, and result of addition – in that order.

public int[,] GetTestTable()
{
// Format of each row:
// A, B, (A-B), (A+B)
int[,] table =
{
{0, 0, 0, 0},
{10, 0, 10, 10},
{0, 10, -10, 10},
{-10, 0, -10, -10},
{0, -10, +10, -10},
{10, 10, 0, 20},
{-10, -10, 0, -20},
{-10, 10, -20, 0},
{10, -10, 20, 0}
};
return table;
}

Following is the function that actually does the testing. We basically process each row returned by GetTestTable one by one. First we set the two inputs on the numeric drop-downs. Next we call the click event-handlers of both buttons verifying what is in the answer label against what we expect. Again notice that we can directly invoke the event handlers and even access properties of the answer label (lblAnswer).

///

///A test for btnSubtract_Click
///

[TestMethod()]
[DeploymentItem("AppUnderTest1.exe")]
public void AddSubtractTest()
{
MainForm_Accessor target = new MainForm_Accessor();

int[,] table = this.GetTestTable();

for (int i = 0; i < table.GetUpperBound(0); i++)
{
int a = table[i, 0],
b = table[i, 1],
subtract = table[i, 2],
add = table[i, 3];

target.numericUpDown1.Value = a;
target.numericUpDown2.Value = b;

target.btnSubtract_Click(null, null);
int result = Convert.ToInt32(target.lblAnswer.Text);
Assert.AreEqual(subtract, result, String.Format(“Subtract {0} and {1}”, a, b));

target.btnAdd_Click(null, null);
result = Convert.ToInt32(target.lblAnswer.Text);
Assert.AreEqual(add, result, String.Format(“Add {0} and {1}”, a, b));
}
}

To run these tests, select Test->Run->All Tests in Solution from the main-menu in Visual Studio. The “Test Results” window in Visual Studio should show that all tests passed.

Summary

There’re multiple ways to go about automating UI testing. You could use an automated testing tool like Mercury Quick Test. However if you want to take things into your own hands and take advantage of greater control, you could write your tests using NUnit or MS-Test and make PInvoke calls to send messages to various controls in the UI of your application under test. You could use the SendKey API in Windows.Forms too.

However if you want to take advantage of knowledge of the internals of the application under test, we have to use Reflection to set properties of the controls, invoke event handlers, call other methods and then test whether this resulted in the changes that we expected. This makes the job extremely painful. I hope this article shows how much easier it is if we leverage Visual Studio Team System to generate accessors for us.

I don’t see why the technique described above cannot be used to test WPF and Silverlight applications as well.

Download
Click here to download the code accompanying this article.

About these ads
Posted in: .NET, General, Tech/Hacks