API Design: Design for Testing
Jakob Jenkov |
One aspect to keep in mind when designing your API is that you will need to test the API, before releasing it. In addition, the user of the API will need to test the parts of her code that uses your API. In other words, you should design for
- Testability of the API itself
- Testability of code that uses the API
Designing for Testability of the API
The easiest way to test code is typically via mock testing. This means that it should be easy to mock up the classes of your API, and easy to inject those mocks into the components you want to test.
To be able to inject mocks into the internals of your classes, you will unfortunately have to expose methods on the class interfaces that enable you to do so. For instance, either a constructor or setter method taking the mock to inject as parameter. To avoid exposing constructors or setters to users of the API, consider making these methods either package access scoped, or protected. If you put your test code in the same package (not necessarily same directory) as the class(es) you need to inject the mocks into, you will be able to access these extra injection methods.
Below is a code example. Imagine that the member variable dependency
does not
need to be exposed to the user of this class. It is only exposed so that a mock implementation
can be injected. It is the protected constructor and setter that expose the dependency
member variable.
public class MyAPIComponent { protected Dependency dependency = null; public MyAPIComponent(){ this.dependency = new DependencyImpl(); } protected MyAPIComponent(Dependency dependency){ this.dependency = dependency; } protected void setMyAPIComponent(Dependency dependency){ this.dependency = dependency; } }
Designing for Testability of Code Using the API
You are not the only one you need to take into consideration when designing your API for testability. Your users will most likely need to test the code that uses your API too. In that respect you should keep in mind that the users will need to create mock implementations of your publicly exposed API classes.
Use Interfaces
The easiest way to make your classes mockable is to have them implement an interface. Dynamic mock API's like Butterfly Testing Tools can then create mock of that interface at runtime, during the unit test. It can also wrap the original implementation, thus merely recording and forwarding all calls to the mock, to the real implementation.
Use Extendable Classes
If you have not, or cannot have your classes implement interfaces, you should consider making the classes easy to subclass at least. That way a mock can be created by subclassing your API classes, and override the methods that need to be mocked / stubbed.
Tweet | |
Jakob Jenkov |