My co-worker and I have been having some conversations about an implementation and could use some help.
I have a class called RulesEvaluator which has some business logic. This class needs some reference data which is available in another class called ReferenceDataHelp. (The ReferenceDataHelp gets the data from a database if it matters).
So now I have the caller code which instantiates the RulesEvaluator and calls a method called runRules on it. All good so far ! Now my question is how should ReferenceDataHelp class be thought of ? I'm thinking it as an implementation detail that only the RulesEvaluator class needs to know and hence the object of ReferenceDataHelp should be instantiated from within the class or the method.
However my co-worker thinks, the ReferenceDataHelp should be instantiated by the caller code and dependency injected into RulesEvaluator to allow for unit testing.
If it makes it easier to unit test, I would favor that approach. That said, there are other way besides dependency injection that you can create seams in your design that allows for unit testing. One way is to keep that ReferenceDataHelp class in a separate deployment unit, e.g. JAR file. You can have one JAR for fake implementations and another for actual implementations.
The problem with instantiating a ReferenceDataHelp instance in the class that's using it is that it violates separation of concerns. Dependency injection separates the concerns of creating a dependency and using a dependency. This allows your designs to be more loosely coupled and more testable. Having the dependency injected makes it easy to inject a fake implementation that you can have more control over during unit testing.
If you are hardcoding the dependency on ReferenceDataHelp in the class that uses it, then you'll have a more difficult time controlling it for unit testing purposes. More difficult but not impossible. The key is to keep the creation of the dependency fairly isolated and easier to override its behavior.
So instead of this:
You'd do this:
Then in the test:
This is the class you can use for testing. The only thing you're replacing is the actual method of getting the ReferenceData, everything else you're exercising in the test comes from the actual RulesEvaluator class.
Yes, they would but that's probably the least cumbersome aspect of that approach. I personally wouldn't attempt that unless there was absolutely no other way to get testability. The more cumbersome aspect is that you'll have yet another build artifact to deal with and a parallel line of code to keep in synch with production and test code. That's why I'd much rather use dependency injection.
I was simply offering that approach to make the point that there are other ways to achieve better testability besides dependency injection.