Sunday, February 10, 2008

More Interfaces...

Recently on projects were I really wanted to test everything I've been creating interfaces more frequently. With object oriented languages interfaces are usually described pretty easily in some fashion, despite the fact that some languages may not directly support them.

This practice of "creating interfaces for everything" means that if I'm interacting with a class that runs processes the only way to test code that depends on this class is to either actually run test processes on the system or create something that pretends to run processes. There are many situations in which running processes is bad, in my case it was the fact the processes in question could take a long time to complete, in other cases you may not want unit tests to depend on the state of the system. So that leaves creating something that pretends to run processes.

Inheritance in languages like Java and C# can be a messy business, in many cases I don't need the complexity imposed on me by API level classes, there's usually only a few methods of an API class that I need and the usage is fairly simple. Because of this implementing a subclass of an API level classes can leave wholes open to the underlying code that I don't necessarily want to expose. In some cases API level classes can't be subclasses (in the case of sealed/final classes in C#/Java). Therefore, we add more indirection, and create an interface.

What good is creating an interface? It can describe exactly how I'm using the API level class and it only exposes the bits of functionality that I need to test. With an interface I can implement a real class that actually runs processes and a fake class that lies about running process and pretends to fail in certain cases in order to test how the code reacts to failure cases. The downside is that creating interfaces everywhere can increase the complexity of the code, more types just means more names and ideas that can contribute to confusion about the overall design of the code.

In the alternate case of subclassing the process class for testing, the code remains simple and the need to create 3 new types is avoided (1 for the interface, 1 for the real implementer, and 1 for the test implementer). However, I think masses of types can still be managed and using interfaces frequently when using hard to test API classes can make code more reliable and easily tested. Interfaces around complex API classes can also improve the descriptiveness of the code, in that the interface can be used to describe how API classes are being used.