Within the past few years, unit testing has taken on a
life of its own. You're not cool if you're not doing a unit test. One
of the hardest choices when creating unit tests is deciding what to
test. When you examine code that you want to test, consider the
following issues:
Public methods: Public methods
are those features of your code that are exposed to the outside world.
Pretty much everyone agrees you should unit test your public methods.
After all, a public method is sitting there saying, "Call me."
Private and protected methods: Private and protected methods
in your code do the work of your public methods. As a result, some
people believe that testing private and protected methods is wasteful.
They believe that unit testing public methods implicitly tests private
and protected methods. Plus, testing private and protected methods is
harder. By their nature, private and protected methods are, well,
private and protected. Unlike public methods, they aren't saying, "Call
me." Just the opposite is true, so you need to go out of your way to
test them.
Interactions with other code:
Unless you're writing all your application's features in a single
function, chances are high your code needs to interact with other code.
The very nature of unit testing, however, is that you want to test only a
single unit of code. How can you do that if you have to interact with
other code?
Public methods are easily tested because, by definition, you can call a public method from anywhere in your code.
People take all kinds of
approaches to solve the problems of testing private methods or code that
interacts with other code. One popular approach is to use software that
specializes in unit testing, usually called unit testing frameworks.
Two approaches to testing code that interacts with other code are stubs and mocks.
1. Letting stubs do the tough testing
By definition, a unit test is
supposed to test only one unit of code. No unit of code lives in
isolation, however. Most developers work around this pesky paradox by
using code stubs, which act as placeholders for units of code that the unit being tested needs to interact with.
The code stub
placeholder is better than interacting with the actual units of code
because each unit of code introduces a new set of variables. Instead,
you can program the code stub to return a consistent set of values
against which the unit can interact. Returning a consistent set of
values creates predictability, which makes it easier to create tests
that you can repeat.
In reality, developers who
use stubs usually go ahead and test the real units of code that interact
with the unit they're testing — that is, as long as it's simple to do
so. For example, say that you create a Customer object with a public method called GetAddress.GetAddress returns an Address object. The Customer and AddressCustomer object to interact with the Address objects are each units of code that have their own set of unit tests. Many developers will create a test that allows their object even though the test extends beyond the unit they want to test.
When the developer encounters a
set of code that is too complicated or messy to interact with a simple
unit test, the developer usually creates a stub. The stub returns a set
of canned values that the developer expects to get from the code.
For example, say that your Customer object has a method called UpdateAddress.UpdateAddress accepts a new address for a customer and passes that data off to an Address object. The Address
object calls a service that validates the address against a valid ZIP
code database. You don't want to have to deal with all that when you're
testing your Customer object.
Instead of using the Address object, you create a stub that returns two possible values. If you pass a valid address, it returns the value ValidAddress. If you pass an invalid address, it returns the value InvalidAddress.
So, how does the stub know what's a valid address and what's an invalid
address? You specify in the stub that 123 Main Street, for example, is a
valid address. Everything else is invalid. Figure 1 shows an example of a stub.
2. Simplifying testing with mocking objects
Other developers believe
that creating a bunch of objects and stubs is too much work. These
developers use a special library to create mock objects. With mock
objects — mocks — you tell the object which methods you expect it to run, how many times the methods will run, and what values to return.
For example, say that you use a mock Address object to interact with your Customer address. For your UpdateAddress test, you tell the mock Address object to call the Address object's Save method. Your test tells the mock object that you're going to send a value of 123 Main Street to the Save method and that you expect it to return a value of true. Your test also tells the mock object that you're going to send the value 123 Nowhere Boulevard and that you expect the Save method to return the value false. Figure 2 shows an example of a mock object.
3. Stubs versus mocks
Deciding which method to
use can be a little difficult at first glance, but several key
differences exist between using mocks and stubs, based on what you want
to test for. Here are some of them:
Mocks
You
tell your mock objects which methods to call, how many times they'll be
called by your test, and what return values you expect.
You create mocks for all objects with which your code interacts.
Mocks require a library of code to create the mock objects.
Stubs
People who use stubs
tend to test clusters of interacting objects together. For example, they
might test all the code related to customers and orders together
because those units of code interact.
People who use mocks, on the
other hand, are more likely to take a top-down approach. They might
start testing at the user interface level first and then use mock
objects to mimic the behavior of business rules or data access code.
They are likely to test first and code second. In other words, they use
testing as a way to discover what code they still need to write to make
their system work.
NOTE
Using mock objects is very
popular in the test-driven development (TDD) style. With TDD, you test
first and code second. For more information, see the Test Driven Web
site at www.testdriven.com.
4. Automating Tests with Testing Frameworks
Unit tests are often grouped so that they can be
executed by a testing framework. A framework isn't necessary, but using a
framework allows you to do these things:
Formalize unit testing.
Create consistency among unit tests.
Make automating testing easier.
Execute groups of tests at one time.
Frameworks are a necessity for
most systems. Because unit tests are so small in nature, even a simple
system has lots of tests. In most cases, using a framework to administer
the tests makes sense. Unit testing frameworks exist for nearly every
programming language under the sun. Popular unit testing frameworks for
the .NET Framework include
NUnit:
By far, NUnit is the most-popular testing framework around. NUnit is an
open source (free) framework. However, a drawback to NUnit is that it
isn't integrated into Visual Studio. Still, many folks use NUnit to
manage their unit tests. You can visit the NUnit Web site at www.nunit.org.
NMock: Creates mock objects for use with unit tests.
TestDriven.NET:
Integrates testing frameworks, such as NUnit, into the Visual Studio
development environment. TestDriven.NET gives you the ability to access
testing commands from a shortcut menu in the code editor. You can visit
the TestDrive.NET Web site at www.testdriven.net.