Using Mocking Frameworks To Help With Unit Testing UI Controls

One of the more common reasons that developers tell us of why they don’t unit test is “All of my application is visual controls with code behind. Refactoring all of that code to a .dll that can be united tested will take more time than it is worth.” While it is true that unit testing is easier if your application lives “in code” as a separate assembly, you can still use unit testing in a UI-heavy code base. By judiciously using a mocking framework, you can speed up the process even more.

Consider this form from a WinForm application written in VB.NET.

image

The grid view has the following code behind :

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click For counter = 0 To DataGridView1.RowCount - 1 If (DataGridView1.Rows(counter).Cells(11).FormattedValue) Then If (DataGridView1.Rows(counter).Cells(10).Value <> "") Then TextBox1.Text = FormatCurrency(TextBox1.Text - DataGridView1.Rows(counter).Cells(10).Value, 2) End If End If Next End Sub

 

The business logic is intermingled with the visual controls (TextBox1, DataGridView1, etc…). Is there a way to easily unit test this code? The answer is yes. Step one is to add a unit test project to the solution. Step two is to break the sub method into a function method. Once the methods have an input and an output, you can put a unit test on it. For example:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click For counter = 0 To DataGridView1.RowCount - 1 Dim initialValue = TextBox1.Text Dim cell0 = DataGridView1.Rows(counter).Cells(11) Dim cell1 = DataGridView1.Rows(counter).Cells(10) TextBox1.Text = GetCalculatedValue(initialValue, cell0, cell1) Next End Sub Public Function GetCalculatedValue(initalValue As String, cell0 As DataGridViewCell, cell1 As DataGridViewCell) As String Dim returnValue As String = initalValue If (cell0.FormattedValue) Then If (cell1.Value <> "") Then initalValue = FormatCurrency(TextBox1.Text - cell1.Value, 2) End If End If Return initalValue End Function

And we can add a unit test like this:

image

[TestMethod] public void GetCalculatedValue_ReturnsExpected() { Form1 instance = new Form1(); String baseValue = "$10.00"; DataGridView gridView = new DataGridView(); gridView.Columns.Add("TEST1", "TEST1"); gridView.Columns.Add("TEST2", "TEST2"); gridView.Rows.Add(new DataGridViewRow()); gridView.Rows[0].Cells[0].Value = "$1.00"; gridView.Rows[0].Cells[1].Value = "$2.00"; DataGridViewCell cell0 = gridView.Rows[0].Cells[0]; DataGridViewCell cell1 = gridView.Rows[0].Cells[1]; var actual = instance.GetCalculatedValue(baseValue, cell0, cell1); var expected = "$8.00"; Assert.AreEqual(expected, actual); }

 

Although this test runs green, it is suboptimal because we have to standup lots of objects (DataGridView, Columns, DataGridRow) just to get to the class we are interested in, in this case DataGridViewCell. Instead of generating all of that superfluous code, there is a better way to set the state of only the class we want – enter Mocking frameworks. Mocking frameworks give us the ability to focus only on the subjects under test (SUT) while ignoring everything else.

But there is a catch. There are 2 types of mocking frameworks: ones that generate their code based on inspecting the types and ones that generate their code based on the compiled IL. The former group includes RhinoMocks and Moq . If you try and add Moq to this unit test project and generate a DataGridViewCell like this:

[TestMethod] public void GetCalculatedValue_ReturnsExpected() { Form1 instance = new Form1(); String baseValue = "$10.00"; var mock0 = new Mock<DataGridViewCell>(); mock0.SetupGet(dataGridViewCell => dataGridViewCell.Value).Returns("$1.00"); var mock1 = new Mock<DataGridViewCell>(); mock1.SetupGet(dataGridViewCell => dataGridViewCell.Value).Returns("$2.00"); var actual = instance.GetCalculatedValue(baseValue, mock0.Object, mock1.Object); var expected = "$8.00"; Assert.AreEqual(expected, actual); }

You will get an exception

image

 

Since we don’t control DataGridViewCell’s code, there is no way to change those properties to overidable/nonvirtual. As a general rule, you only use RhinoMocks/Moq on classes that you can control.

The other type of mocking framework (based on IL) can solve this problem. There are 2 commercial frameworks (JustMock, TypeMock) but they cost $399/year (as of this writing). There is a 3rd framework we can use and it is built into Visual Studio 2012+. It is called the Microsoft Fakes Framework. By adding this to your test project,

image

you can craft your unit test like so:

[TestMethod] public void GetCalculatedValue_ReturnsExpected() { Form1 instance = new Form1(); String baseValue = "$10.00"; using (ShimsContext.Create()) { var cell0 = new ShimDataGridViewCell(new StubDataGridViewCell()); cell0.FormattedValueGet = () => { return "$1.00"; }; var cell1 = new ShimDataGridViewCell(new StubDataGridViewCell()); cell1.ValueGet = () => { return "$2.00"; }; var actual = instance.GetCalculatedValue(baseValue, cell0, cell1); var expected = "$8.00"; Assert.AreEqual(expected, actual); } }

and get green. The downside of using Microsoft Fakes is that you need to re-generate the fakes if the code changes. This makes it ideal for faking external libraries that don’t change much (like ADO.NET) but not assemblies that are under active development.

Leave a comment