Many of the application I work on make use of api's to servers, communications engines and so on. I find it useful to be able to test my application with mock API's so I have more control of the tests on it and can test it standalone. Also the mechanism provided allows me to run my application when a service has yet to be delivered but is defined. I could make use of dependency injection if I was to drag JEE api's into my build but I prefer the simple approach. It might not be perfect but it works.
Lets say I have an external interface that my application makes use of:
public interface Service {
void addListener(final ServiceListener listener);
void removeListener(final ServiceListener listener);
}
Now in my application I would typically construct a concrete class either directly or via a factory method which implements the interface contract. Whilst testing I would like to actually provide another version of the implementing class which can be used to test my application with the interface:
public class MockService implements Service {
private static List<ServiceListener> listeners = new ArrayList<ServiceListener>();
public void addListener(ServiceListener listener) {
listeners.add(listener);
}
public void removeListener(ServiceListener listener) {
listeners.remove(listener);
}
public void newRequestReceived(final ExternalRequest request) {
RequestEvent event = new RequestEvent(request);
for(ServiceListener listener : listeners) {
listener.notifyRequestEvent(event);
}
}
}
I provide the mock class making use of a command line property:
java -Dservice.api.classname=com.webbyit.MockService -jar myapp.jar
Now in the code of my app I make use of the property (maybe within a factory method) and construct the class provided rather than the default class:
private static Service getService() {
synchronized (ServiceLock) {
if (Service == null) {
String serviceClassName = System.getProperty("service.api.classname");
if (serviceClassName == null) {
Service = new RealService();
} else {
try {
Service = (Service) Class.forName(serviceClassName).newInstance();
} catch (InstantiationException e) {
throwCallServiceInitialisationException(serviceClassName, e);
} catch (IllegalAccessException e) {
throwCallServiceInitialisationException(serviceClassName, e);
} catch (ClassNotFoundException e) {
throwCallServiceInitialisationException(serviceClassName, e);
}
}
}
}
return Service;
}
}
So what I have done is test for an alternative class being specified by a property (via the command line). If set the code attempts to construct the alternative class and use that instead of the default class usually used by the application.