BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Testing
.NET 3.5+

Mocking Static and Sealed Types Using Wrappers

A key concept in unit testing is isolating the code under test from its dependencies. However, when those dependencies are static or sealed types, creating appropriate test doubles, such as stubs or mocks, becomes more difficult.

Static and Sealed Methods

When you are developing software using test-driven development (TDD), or when you are adding unit tests to code that already exists, it is important that you initially isolate the code under test from its dependencies. It may be that over time and with refactoring an individual test may exercise more than one type but at first it is common to replace dependencies with test doubles, such as stubs, fakes or mock objects. During testing these test doubles are designed to give predictable results that simplify the tests. At run-time, the real dependencies are provided using techniques such as dependency injection, the service locator design pattern or an inversion of control (IoC) container.

Creating a stub, fake or mock usually relies upon the dependency in question meeting certain criteria. The most common is that a class being faked implements an interface that includes all of the methods and properties that will be called by the code under test. A test double can then be developed that responds in an expected fashion to these calls. Another option is that the dependency is derived from an abstract class. In this case, a mock that inherits from the same class can become the test double.

Problems arise when the dependency is a sealed type. As test doubles generally rely on inheritance and polymorphism, and sealed classes don't permit these, most mocking frameworks cannot generate a suitable mock object. You can't create your own custom class for injection either, as you will be unable to inherit from the sealed type. A similar problem exists for static classes, or static members of instance types.

Using Wrapper Classes

There are several manners in which you can avoid the problems of creating test doubles for sealed and static types. One way is to avoid creating such types in the first place. Many developers follow this approach to improve testability. However, although these types are usually unnecessary, there are situations where sealed and static types are very useful. For example, you might develop an encryption system based upon a security algorithm within a class. If another type could inherit from such a class, it could override the algorithm and cause vulnerabilities. A sealed class might be more suitable. A second problem with the approach of not using static and sealed types is that the .NET framework includes both. It is highly unlikely that you would be able to avoid all of these classes and remain productive.

Another approach to the static / sealed problem is to use an isolation framework that permits mocking of such types. There are several commercially available tools for this purpose, which usually work by modifying the intermediate language (IL) code after compilation. Some developers argue that these tools promote bad coding practices or poor design, whilst others see no issue with their use. However, the tools are often expensive, which may put them beyond the reach of those developing on a budget or for hobbyists.

A third option for creating test doubles for static and sealed types is to create wrapper classes for the dependencies. This is the approach we'll demonstrate shortly. Here, a very simple class is created that includes similar members to the dependency. The class is often paired with a matching interface, which can make mocking easier.

For static classes, the wrapper defines all of the methods that the code under test needs to call. Each member calls the corresponding static member and returns any result. Test doubles inherit from the matching interface and are injected into the class under test. At run-time, the simple wrapper class is injected.

When wrapping sealed types, the wrapper class includes a field that holds an instance of the sealed version at run time. During testing, an interface is used as the basis for stubs, fakes or mocks.

Example Wrapper

To demonstrate, we'll create a wrapper for a static class. We'll use the File class, which contains a method named, "ReadAllLines". This method reads a text file and returns an array of strings, with one string for each line of text in the file. We'll create and test a very simple class that counts the lines of text in a file using ReadAllLines.

To begin, we need to create an interface for the wrapper. Depending upon your requirements, you might include a subset of the members of the class being wrapped, or you might include duplicates of every member. As we only need one method for this example, we'll only include ReadAllLines in the interface. We'll make the definition of the method identical to the underlying signature, except that it will not be static.

public interface IFileWrapper
{
    string[] ReadAllLines(string path);
}

We can now create an implementation of the wrapper. This will be injected at run-time. You can see that the code simply calls the corresponding method from the File class and returns the results.

public class FileWrapper : IFileWrapper
{
    public string[] ReadAllLines(string path)
    {
        return File.ReadAllLines(path);
    }
}

NB: The wrapper class has no unit test coverage. You might deem it necessary to include an integration test that reads a known file from disk. However, as the wrapper only calls the underlying method, most developers would see the lack of coverage as acceptable.

10 September 2013