In a previous post I had said we had
…changed our can’t-mock-dis Startup service from a plain ol’ Singleton…to an Abstract Factory so that…we can now mock out the Startup service and its application-context-creatin’ slowness when we want to.
But some muddled thinking had snuck in on my part: New Ben notes that you don’t need a factory to mock an interface. Why didn’t we just make the StartupSvc class an interface and mock that interface? Why go to the bother of creating a StartupSvcFactory as well?
Here’s the proof:
- Assume that a factory is not needed
- StartupSvc is a class, and getStartupSvc() is a static method on it
- You can’t have a static method on an interface (a rule of the Java language), and a factory is not needed (by assumption, #1), so we’ll make getStartupSvc() an instance method of StartupSvc.
- To call getStartupSvc() as an instance method, you must already have an instance of a StartupSvc on which to call it, BUT because StartupSvc is a singleton, the only way to get an instance in the first place is by calling getStartupSvc(). When getStartupSvc() as an instance method, there is no way to call it.
∴ a factory is needed.
The factory then takes care of managing the singleton-ness of the StartupSvc and holds the static StartupSvc reference.
How does this help mockability? Client code then holds a reference to a StartupSvcFactory (interface) and is injected with a StartupSvcFactoryImpl whose getStartupSvc() instance method returns a real StartupSvcImpl — but unit tests inject a mock StartupSvcFactory and can then simulate different behaviors coming from the StartupSvc that are difficult to reproduce, or just slower, with the real thing.