Designing Constructors for Testability
constructor, java, refactoring, unit-testing
Solution
Look at using a Dependency Injection framework such as Spring. This application of Inversion of Control means that each of your types can avoid the pitfall you've seen, leaving the configuration to "wire" components together.
This introduction to Spring (pdf) gives a comprehensive overview of Spring. The first chapter or two should be sufficient to explain the concepts.
Also see Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler
Problem
I'm working with some existing code, trying to add to it and increase the unit tests for it. But running into some problems with getting the code testable. Original Constructor: ``` public Info() throws Exception { _ServiceProperties = new ServiceProperties(); _SshProperties = new SshProperties(); } ``` I'm aware that this is bad, and obviously not testable. In a junit environment, this class will fail to create every time since it wont be able to find the necessary properties to construct itself. Now, I'm aware this class would be a lot more testable with the simple change of moving anything prefaced with 'new' as a parameter. So I end up with: New Constructor: ``` public Info(ServiceProperties srvProps, SshProperties sshProps) throws Exception { _ServiceProperties = srvProps; _SshProperties = sshProps; } ``` Which allows me to properly unit test this Info class. The problem though, is now all that work is pushed to some other class: Some Other Class' Method: ``` public void useInfo() throws Exception { ServiceProperties srvProps = new ServiceProperties(); SshProperties sshProps = new SshProperties(); Info info = new Info(srvProprs, sshProprs); doStuffWithInfo(info); } ``` Now this method isn't testable. All I've managed to do is push off where the constructions of these Property objects are occurring, and somewhere else some piece of code is going to be stuck actually having to call "new". Here's the rub for me: I can't figure out how to break this chain of events of simply pushing these "new" calls somewhere else. What am I missing?