Unit-Test Patterns for .NET – Part I

Abstract 

This article looks at unit testing patterns and describes the main patterns found in tested .NET code. It also describes the problems with each pattern. We will be using nUnit for our examples. For more information, see nUnit and Test Driven Development. Read the below nUnit tutorial to get more information on Nunit Unit testing and download Typemock Isolator for .NET unit testing.

Introduction

Programmers who have incorporated unit testing into their development process already know its advantages: cleaner code, courage to refactor, and higher speed. Writing tests is easy; it is writing good tests that makes automated unit testing a powerful tool. This article provides some basic testing patterns that help write good tests, although some cases may need special attention. But first, we must understand what a good test is. The following is a list of features of a good test:

  • Isolation:
    Tests should test only the code that we plan to test. We should not test underlying classes, which should be checked independently. Failure to test isolated code will lead to high coupling between tests, and changing a class may cause many unrelated tests to fail. The normal scope is a class, although sometimes it makes more sense to test a cluster of classes that are closely coupled (functionality wise).
  • Speed:
    If running the tests takes too long, then developers are reluctant to run them. Therefore, we should keep our tests as fast as possible.
  • Self containment:
    Tests that rely on external information are prone to fail and require configurations and setups. This leads to test discarding. To make sure that this doesn’t happen, all the information required for the tests should be in the tests. For example, instead of relying on an environment variable to be set, we must set it ourselves in the test.
  • Race safe:
    Multiple developers should be able to execute tests at the same time. When a test depends on a shared state, it can lead to the wrong test results. For example, if there is a test that depends on and modifies a database and several developers are running it simultaneously, the data will be corrupted and the test will fail.
  • Independence:
    Tests should not rely on other tests to be run before or after. They must be able to be run alone so that the developer can run a single test at a time.
  • Well documented:
    Good documentation about what the test is doing will help understand the production code. Moreover, many developers don’t like to read the documentation and prefer to see an example of how to use the code. This is fine, and the tests can be a reference point.
  • Maintainable:
    Tests should be seen as part of the software code even though it doesn’t make it to production. As such, it must be maintainable and refactored when needed.

Patterns, Patterns, Patterns

Throughout this article, we will use an example of an Authentication class. As it name suggests, this class is responsible for authentication in our system. Each pattern description is divided into three parts:

  • When to use the patterns – what are the cases in which we will use the pattern
  • How to use the pattern – what are the recommended steps of the pattern
  • Example – example code in C#

We will now delve into the patterns.

Four-Stage Testing Pattern

When should it be used?

This is the normal testing scheme.

How should be it used?

  1. Setup prerequisite objects: Create the scenario of the test (this can be done in the test method or in the [SetUp] and[TestFixtureSetUp] methods).
  2. Call the method being tested.
  3. Evaluate the results.
  4. Tear down the objects.

Example

This is our test:

Test-Exception Pattern

When should it be used?

When we expect our test to raise an exception.

How should it be used?

Use nUnit’s [ExpectedException] attribute.

Example

The following code snippet shows an example of how to test exceptions:

We expect our test to throw ValidationException with the “Password is empty” error message.

Inner-Class Test Pattern

When should it be used?

When a protected entity (field or method) needs to be accessed for the test. This can happen in two situations:

  1. When we need to test a protected method or access a protected field.
  2. When we need to override a public or protected method (can be done only if the method is virtual).

How should it be used?

Create a new class that extends our tested class. 
To test a protected method, add a method in our extended class that calls the protected method (delegation).
To override a method, override this method in our extended class.

Example – Testing a protected method

The following code snippet shows an example of how to test the protected EncodePassword method.

Example – Overriding a protected method

Let’s go back to our example. Suppose that Authentication has the following methods (the method DoSelect returns the number of lines that the one selected returned):

We want to check that IsAuthenticated is behaving correctly but without setting up a database of passwords. We can override the DoSelect method and return our own values. We can then test if DoSelect works using the Inner Class Test Pattern for testing a protected method.

Reflection Test Pattern

When should it be used?

When we need to test private methods.

In most cases, we don’t need to test private methods. It is enough to test the public ones, although there are some cases when this is needed. For example, if we have a perfect binary tree class with get and put methods, we can check that the tree works, but we still need to test that the tree is perfect. This can be done only if we test the private method that resorts the tree.

How should it be used?

Use reflection.

Example

Mock Object Pattern

Mock objects are objects that replace the real objects and return hard-coded values. This helps test the class in isolation.

There is a difference between mocks and stubs. A stub is used to simulate an object. Stubs are typically used to stub out objects that are expensive to create or manipulate, whereas mocks are used to test interactions between classes. This is a major difference. There’s a good article by Martin Fowler about mocks and stubs:http://martinfowler.com/articles/mocksArentStubs.html.

Mocks are used to validate that the interactions between objects behave as expected.

 Do not attempt to reproduce real behavior in a mock object; if they are becoming complex, revisit both the mock implementation and the code being tested to see if there is some intermediate concept to be factored out.

Dynamic mock frameworks are frameworks that build mock objects on the fly. There are quite a few mock object frameworks; the difference between them is in the way the developer writes the interactions with the object.

When should it be used?

Mock objects can be used in the following cases:

  • The real object has a nondeterministic behavior (it produces unpredictable results, like a date or the current weather temperature)
  • The real object is difficult to set up
  • The behavior of the real object is hard to trigger (for example, a network error)
  • The real object is slow
  • The real object has (or is) a user interface
  • The test needs to ask the real object how it was used (for example, a test may need to confirm that a callback function was actually called)
  • The real object does not yet exist (a common problem when interfacing with other teams or new hardware systems)

How should it be used?

 The three key steps to using mock objects for testing are:

  1. Use an interface to describe the object.
  2. Implement the interface for production code.
  3. Implement the interface in a mock object for testing.

Example – using the nUnit.mock framework

Here is a test:

1 Create a mock for the ISubscriber interface.
2 Get the actual object.
3 Expect Receive() to be called once with the “user” string as a parameter.
4 Verify that the mock was called as expected.

 There are mock frameworks that implement mock objects for common types (System.Data, for example).

Summary

In this article, we introduced the common unit test patterns. These patterns can be use alone or in combinations. For example, you want to mock a database connection but it is created in a protected virtual method. The test will use the Inner Class Pattern to return the mock database object with a mock object for the actual database.

There are still many situations in which these patterns are not sufficient and there is a need to change the code to make it testable. These include:

  • Singleton classes
  • Calls to static members
  • Objects that do not have an interface (as required for mock-object pattern)
  • Objects that are instantiated in the code being tested
  • Objects that are not passes to the method (as required for mock-object pattern).

These will be covered in Part II – Type Mock Pattern.