Test Your DotNet GUI with NUnit and Mock Objects

Unit testing is an easy technology to learn, but very difficult to master. In particular, problems often occur when developers try to start testing user interfaces, modules that are not complete yet, database code, or code that depends on network interactions. There are various ways to solve these kinds of problems, but one of the most interesting involves the use of mock objects.

This article provides a brief introduction to the syntax and basic principles of mock objects. Anyone who is already familiar with the basic principles of unit testing should be able to follow this article with no difficulty. This article differs from most of the other introductions to mock objects found on the web in that it goes beyond showing you the simple syntax for using mock objects and focuses on introducing the rationale behind this school of programming. Other articles found on the web show you how to write the syntax for creating mock objects, but don’t explain why you are creating them and what kinds of problems they solve. This article attempts to flesh out this subject matter by discussing more than the basic syntax, and hence giving you a start on understand how and when to correctly design applications that can be tested with mock objects.

The theory behind mock objects is a relatively deep subject that can be discussed at considerable length. However, one needs a place to start an in depth discussion, and the goal of this article is to give you a basic understanding of the technology so that we can examine it in more depth at a later date. In particular, this article demonstrates how to use mock objects to test code that has heavy dependencies on a graphical user interface element.

This article does not enter into advanced discussions of mock theory, test isolation, interaction tests, state tests, and mock objects vs stubs. That type of subject matter will be addressed in additional articles to be written at a later date. When reading about these advanced matters, you will retroactively see why starting out by learning how to mock up graphical objects is a good idea. You will also find that mock objects are great tool for writing stubs.

NOTE: In this article I will show how to use the lightweight implementation of mock objects that is built into NUnit. I chose to do this because NUnit is widely distributed, widely understood, and easy to use. If you read this article, and think that you want to use mock objects in your own code, you might consider using NMock, DotNetMock, EasyMock.NET, NCover or a commercial mock object implementation such as TypeMock. I believe, however, that you would be wise to start out learning about mock objects using the NUnit code shown here, and then apply that knowledge to more advanced tools once you understand the basics. There is nothing wrong with the lightweight mock object framework provided with NUnit, and if it suits your needs, then you can safely use it for all your testing.

The article begins with an explanation of what mock objects are and presents a simple example of one kind of problem they are designed to solve. Then you will see how to handle the simple syntax involved with creating a mock object using NUnit. If you don’t want to read some useful and easy to understand theory about how mock objects work, then you can skip right to the sections on understanding the syntax and implementing mock objects. The two key code samples are Listing 1 and especially Listing 2.

Introduction to Mock Objects

You will never be able to unit test your code unless you design it properly. The key to creating code that can be unit tested is to ensure that you engage in loose coupling. Loosely coupled code is code that can be easily decomposed into discreet objects or packages/assemblies. If your code is all bunched together into one monolithic ball and you can’t initialize one section of it in isolation from the rest, then your code is not loosely coupled. Code that is not loosely coupled is difficult to test

When creating loosely coupled code, usually it is helpful to provide interfaces for the key objects in your program. The ideal is to have loosely coupled objects that can be initialized in isolation from one another, and that can be accessed through interfaces. Loosely coupled code of this type is both easy to maintain and easy to test.

Loosely coupling your code is particularly important when it comes to working with hard to test areas such as interfaces and databases. Be sure that you create code that gets input from the user in one class, and code that performs operations on that data in a second class. A quick metric to use when designing classes of this type runs as follows: Be sure that each class you create performs one, and only one, major task.

Working with GUI Interfaces

It helps to look at a specific example when thinking about what it mean to perform one and only one major task in a class. At the same time, we will see how to separate easily testable code from difficult to test graphical user interface code.

Most dialogs have a button labeled OK that the user presses after the user has entered data. To properly unit test your code, you need to make sure that data is transferred from the dialog class that contains the OK button to a separate class the holds the data. This ensures that your user interface supports only the task of getting data from the user, and does not also try to store that data or perform operations on that data. It is this second class that will prove to be easy to test.

NOTE: It is important to properly separate the task of getting the input from the user from the task of performing operations on that input. For instance, if you have code that ensures that a user can only enter digits in an input box, then that code belongs with the input dialog; it is part of getting input from the user. If however, you want to store that data in a database, or if you want to perform a mathematical calculation on that data, then you want to move such code out of the input dialog before attempting to store it in the database, and before you perform calculations on it.

Most people who write code of this type without planning ahead will create a dialog that mixes the tasks of receiving input from the user with the task of performing operations on that data. By doing so the commit two errors:

  1. The have one class perform two major tasks.
  2. The put code that needs to be tested inside a GUI interface class that is hard to test.

Our natural instincts lead us astray when we write this type of code. It takes a conscious effort to begin to properly design applications that have a user interface.

The objection to the idea of separating data operations from user input operations is that it requires writing additional code. Instead of writing just one class, you now have to write two classes: one class for the input dialog, and one for holding the data and performing operations on it. Some developers object that writing the additional code takes more time, and it ends up bloating the code base for a program. The reposte is simply that one needs to choose: do you want to write less code or do you want to write code that is easy to test and maintain? My personal experience has shown that it is better to have code that is easy to test and maintain.

NOTE: Just to be absolutely clear: The primary reason to split up your code into two classes is to make it easy to maintain. The additional benefit of making the code easy to test simply falls out naturally from that initial decision to support a good architecture. I should add that you usually don’t need to unit test the graphical user interface itself. The people who created your GUI components did that for you. When was the last time you had a input box malfunction on you? It just doesn’t happen. The code we need to test is the code that performs operations on our data, not the code that gets the data from the user.

Enter the Mock Object

If you have decided to properly decompose your code into separate classes for the GUI and for containing your data, then the next question is how one goes about testing such code. After all, the code that contains your data still needs a way to obtain input. Something has to feed it data. In a testing scenario, if you decide to get input for the data class from the interface module, then you are no better off than before you decomposed your code. The dialog is still part of your code, and so you are still stuck with the difficulty of automating a process that involves getting input from the user. To state the matter somewhat differently, what is the point of promoting loose coupling if you don’t ever decouple your code?

The solution to this dilemma is to allow something called a mock object to stand in for your input dialog class. Instead of getting data from the user via the input dialog, instead, you get data from your mock object.

If your code were not loosely coupled, then you could not remove the input dialog from the equation and substitute the mock object for it. In other words, loose coupling is an essential part of both good application design in general, and mock object testing in particular.

At this stage, you have enough background information to understand what mock objects are about, and what kind of problem they can solve. Exactly how the syntax for creating mock objects is implemented is the subject of the remaining sections of this article.

Writing Mock Objects

Now that you understand the theory behind mock objects, the next step is to learn how to write a mock object. I will first explain how the syntax works, then show how to implement a mock object.

Understanding the Syntax

Mock objects are generally built around C# interfaces. (I’m now talking about the C# syntactical element called an interface; I’m not talking about graphical user interfaces.) In general, you want to create an interface which fronts for the object that you want to mock up.

Consider the case of the input dialog we have been discussing in this article. You will want to create an interface that can encapsulate, as it were, the functionality of that input dialog. The point here is that it is awkward to try to use NUnit to test dialogs of this type, so we are creating a mock object as a substitute for this dialog. As you will see later in this article, creating the interface is a key step in the process of developing our mock object.

Suppose you have an input dialog  that gets the user’s name and his or her age. You need to create an interface that would encapsulate this entire class.

 

The input dialog that we want to mock up with our mock object.

Here is an interface that can capture the information from this dialog:

public interface IPerson{	string UserName { get; }	int Age { get; }} 

The InputDialog should implement this interface:

	public class InputDialog : System.Windows.Forms.Form, IPerson	{		private int age;		private String name;
		public int Age		{			get { return age; }			set { age = value; }		}
		public String UserName		{			get { return name; }			set { name = value; }		}

Note in particular that InputDialog descends from System.Windows.Forms.Form, but it implements IPerson. The complete source for this class can be found here.

The class that will contain and perform operations on the data from the InputDialog will consume instances of IPerson. The full source code for this class, called PersonContainer, will be shown and discussed later in this article.

	public class PersonContainer	{		IPerson person;

		public PersonContainer(IPerson person)		{			this.person = person;		}

Now you can create an instance of your dialog and pass it to your data container after the user inputs data:

	private void button1_Click(object sender, System.EventArgs e)	{		InputDialog inputDialog = new InputDialog();		inputDialog.ShowDialog(this);		PersonContainer personContainer =
		  new PersonContainer(inputDialog);	}

If you are not used to working with interfaces, please examine this code carefully. The variable inputDialog is of type InputDialog. Yet notice that we pass it to the constructor for PersonContainer, which expects variables of type IPerson:

public PersonContainer(IPerson person)

This works because InputDialog supports the IPerson interface. You can see this by looking at the declaration from for InputDialog:

public class InputDialog : System.Windows.Forms.Form, IPerson

The key point to grasp here is that the constructor for PersonContainer doesn’t care whether the variable passed to it is of type InputDialog or of type FooBar, so long as the class supports the IPerson interface. In other words, if you can get it to support the IPerson interface, then you can pass in a variable of almost any type into PersonContainer’s constructor.

By now, the lights should be going on in your head. In our production program, we are going to pass in variables of type InputDialog to PersonContainer. But during testing, we don’t want to pass in InputDialogs, because they are graphical user interface elements, and are hard to test. So instead, we want to create a mock object that supports the IPerson interface and then pass it in to PersonContainer. Exactly how that is done is the subject of the next two sections of this text.

Implementing the Data Object

Before we create the mock object, we need to see the data object. This is the object that will consume both the InputDialog, and the mock object. In other words, this is the object that we want to test.

It is usually best to put code like this into a separate assembly. Again, we do this because we want to support loose coupling. You want your primary project to contain your main form, and the InputDialog and PersonContainer reside in a separate assembly.

NOTE: Right now, you can see more clearly than ever just why so many people do not adopt unit testing, or fail when they attempt to adopt it. We all talk about getting the architecture for our applications right, but in practice we don’t always follow the best practices. Instead, we take short cuts, falsely believing that they will "save time."

the structure for your project as it appears in the Solution Explorer. Notice that the main program contains a form called MainForm.cs, which in turn calls into InputDialog and PersonContainer. These latter object are both stored in a separate assembly called LibraryToTest.

 

The structure of the project after it has been properly designed to contain a main program and a supporting library. The code that we want to test resides in its own library where it is easy to use.

Notice the references section in the library contains System.Drawing and System.Windows.Forms. I had to explicitly add these, as they were not included by default. To add a reference, right click on the References node in the Solution Explorer and bring up the Add References dialog. Add the two libraries.

 

Choose Project | Add Reference to bring up this dialog. Double click on items in top of the dialog to move them down to the Selected Components section at the bottom of the dialog.

Listing 1 shows a simple object called PersonContainer that could consume objects such as InputDialog that support the IPerson interface. Notice that I store both the interface and the data container in this one file.

Listing 1: The source code for the class that you want to test. It consumes objects that support the IPerson interface.

using System;

namespace CharlieMockLib
{
    public interface IPerson
    {
        string UserName { get; }
        int Age { get; }
    }

    public class PersonContainer
    {
        IPerson person;

        public PersonContainer(IPerson person)
        {
            this.person = person;
        }

        public String SayHello()
        {
            return "Hello " + person.UserName;
        }

        public String DescribeAge()
        {
            return person.UserName + " is " + person.Age + " years old.";
        }

    }
}

Be sure you understand what you are looking at when you view the code shown in listing 1. This is code that we want to test. The most important point is that in your main program it will have a dependency on a GUI interface element which in this case is called InputDialog. It is hard to unit test a GUI element such as a dialog, so we are working around that problem by creating a mock object and passing it instead of the InputDialog. To make this possible, we have defined an interface called IPerson which is supported by both InputDialog and our mock object.

NOTE: From here on out, you need to have NUnit installed on your system in order to follow the code examples. NUnit is a free open source project.

Implementing the Mock Object

From the discussion in the previous sections, you can surmise that it would not be difficult to manually create a class that supports IPerson and would therefore act as a mock object that you can pass in to your data container. Though not difficult intellectually, performing tasks of this type can become a monotonous exercise. What the NUnit mock object classes do for you, however, is to make it easy for you to create a mock object. The take the pain out of the process.

By now, you are anxious to see the mock object itself. Begin by creating a new class library and adding it to the solution that you want to test. Add the nunit.framework and nunit.mocks to the references section of your class library. If these two items do not appear in the Add Reference dialog, then you need to press the Browse button, and browse to the place where you installed nunit. You will find nunit.framework.dll and nunit.mocks.dll in the nunit bin directory.

 

Adding the references to nuit.framework and nunit.mocks to your project. You can reach this dialog right clicking on the references section shown in Figure 05.

After you have added these two assemblies to your project, you should see them in Solution Explorer.

 

Viewing the references sections of your project in the Solution Explorer. Note that you can see both nunit.framework and nunit.mocks.

Now that you have added the libraries necessary to support NUnit, you are ready to write the code for creating a mock object. After all this build up, you might expect this code to be fairly trick. In fact, you will find that it is quite straightforward, as you can see in Listing 2.

Listing 2: The code for the mock object.

using System;

namespace MockObjectTest
{
    using System;

    namespace NUnitMockTest
    {
        using NUnit.Framework;
        using CharlieMockLib;
        using NUnit.Mocks;

        [TestFixture]
        public class NUnitMockTest
        {
            private const String TEST_NAME = "John Doe";

            public NUnitMockTest()
            {
            }

			  [Test]
			  public void TestPersonAge()
            {
                DynamicMock personMock = new DynamicMock(typeof(IPerson));
                PersonContainer personContainer =
                    new PersonContainer((IPerson)personMock.MockInstance);

                personMock.ExpectAndReturn("get_UserName", TEST_NAME);
                personMock.ExpectAndReturn("get_Age", 5);            

                Assert.AreEqual("John Doe is 5 years old.",
					personContainer.DescribeAge());
                personMock.Verify();
            }
        }
    }
}

The code uses nunit.framework and nunit.mocks: It also depends on CharlieMockLib, which is the namespace in which the PersonContainer shown in Listing 1 resides:

using NUnit.Framework;
using CharlieMockLib;
using NUnit.Mocks;  

You can see that the [TestFixture] and [Test] attributes are added to our code, just as they would be in any unit test.

The first, and most important, step in creating a mock object is to create an instance of the DynamicMock class. The NUnit DynamicMock class is a helper object that provides an easy way for us to "mock" up an implementation of the IPerson Interface. Here is an example of how to construct an instance of this class:

DynamicMock personMock = new DynamicMock(typeof(IPerson));

Notice that we pass in the type of the IPerson interface. We are asking the NUnit mock object implementation to create an object for us that will automatically and dynamically support the IPerson interface.

The next step is to retrieve an instance of our mock object from its factory and pass it in to the PersonContainer:

IPerson iPerson = (IPerson)personMock.MockInstance
PersonContainer personContainer = new PersonContainer(iPerson);

If you want, you can save a little typing by doing this all on one line:

PersonContainer personContainer =
  new PersonContainer((IPerson)personMock.MockInstance);

Now we need to initialize the values for the two properties on the IPerson interface we have created:

private const String TEST_NAME = "John Doe";

personMock.ExpectAndReturn("get_UserName", TEST_NAME);
personMock.ExpectAndReturn("get_Age", 5);

Calls to ExpectAndReturn inform our mock object of the properties that we plan to call, and the values that we want our mock object to return. The first parameter in the first call informs our mock object that we plan to call the UserName property exactly once, and that we expect it to return the value John Doe. The second call to ExpectAndReturn does the same type of thing for the Age property. In terms of our whole project, you can think of these two lines as saying: "Pretend that the user popped up the InputDialog and entered the value John Doe for the user name, and the value 5 for the age." Of course, the input dialog is never used.

NOTE: I find it peculiar that NUnit wants us to pass in get_ prefixed to the name of properties that we want to call. Other implementations of mock objects do not require that you prefixget_ before calling a property.

The final step in this process is to run our actual test to see if our container properly handles input from our mocked up instance of InputDialog:

Assert.AreEqual("John Doe is 5 years old.", personContainer.DescribeAge());
personMock.Verify();

As you can see, the PersonContainer calls each of these properties exactly one time:

public String DescribeAge()
{
  return person.UserName + " is " + person.Age + " years old.";
}

The call to Verify will fail if the UserName or Age properties are called more than once. This can happen if there is an error in your code, or if you view one of the properties in the watch window of your debugger.

Summary

This article gave a (warning: oxymoron ahead) detailed overview of how to use mock objects. The majority of the article was dedicated to explaining why you would want to use mock objects, and in explaining how they can be used to solve a particular type of problem. The actual implementation of a mock object took up less than half of this article.

I should point out three important facts:

  1. Mock objects are not designed solely for solving the problem of testing the graphical user interface for an application. They are also used for mocking up database access, network access, or incomplete parts of large projects. Many developers, particularly in the XP tradition, use mock objects for all the secondary layers in their application. In other words, whenever one object in a program depends on another object from your program, then these hardcore mockers use mock objects.
  2. The NUnit mock objects are not the only solution for testing a graphical user interface. In particular, there are commercial products such as TypeMock that offer advanced facilities and greater ease of use. Furthermore, various tools, including TestComplete, (a company in which Falafel is a part owner), can also be used for testing user interfaces. Many of these commercial testing tools provide shortcuts that may be easier to use than the process shown here.
  3. As mentioned earlier in this article, the NUnit implementation of mock objects is lightweight. In particular, the release notes for NUnit state: "This facility is in no way a replacement for full-fledged mock frameworks such as NMock and is not expected to add significant features in upcoming releases. Its primary purpose is to support NUnit’s own tests. We wanted to do that without the need to choose a particular mock framework and without having to deal with versioning issues outside of NUnit itself." I feel compelled to add, however, that if the NUnit mock objects shown in this article meet your needs, there is no reason for you to upgrade to another tool.

Mock objects can play a very important role in unit tests. Hopefully this brief introduction to the topic should give you the information you need to use them in your own testing process.

No comments yet

Leave a Reply

You must be logged in to post a comment.