I have to say that in my opinion, the entire notion of Dependency Injection is overrated.
DI is the modern day equivalent of global values. The things you are injecting are global singletons and pure code objects, otherwise, you couldn't inject them. Most uses of DI are forced on you in order to use a given library (JPA, Spring Data, etc). For the most part, DI provides the perfect environment for nurturing and cultivating spaghetti.
In all honesty, the easiest way to test a class is to ensure that all dependencies are created in a method that can be overridden. Then create a Test class derived from the actual class and override that method.
Then you instantiate the Test class and test all its methods. This won't be clear to some of you - the methods you are testing are the ones belonging to the class under test. And all of these method tests occur in a single class file - the unit testing class associated with the class under test. There is zero overhead here - this is how unit testing works.
In code, this concept looks like this...
class ClassUnderTest {
protected final ADependency;
protected final AnotherDependency;
// Call from a factory or use an initializer
public void initializeDependencies() {
aDependency = new ADependency();
anotherDependency = new AnotherDependency();
}
}
class TestClassUnderTest extends ClassUnderTest {
@Override
public void initializeDependencies() {
aDependency = new MockitoObject();
anotherDependency = new MockitoObject();
}
// Unit tests go here...
// Unit tests call base class methods
}
The result is exactly equivalent to using DI - that is, the ClassUnderTest is configured for testing.
The only differences are that this code is utterly concise, completely encapsulated, easier to code, easier to understand, faster, uses less memory, does not require an alternate configuration, does not require any frameworks, will never be the cause of a 4 page (WTF!) stack trace that includes exactly ZERO (0) classes which you wrote, and is completely obvious to anyone with even the slightest OO knowledge, from beginner to Guru (you would think, but would be mistaken).
That being said, of course we can't use it - it's too obvious and not trendy enough.
At the end of the day, though, my biggest concern with DI is that of the projects I have seen fail miserably, all of them have been massive code bases where DI was the glue holding everything together. DI is not an architecture - it is really only relevant in a handful of situations, most of which are forced on you in order to use another library (JPA, Spring Data, etc). For the most part, in a well designed code base, most uses of DI would occur at a level below where your daily development activities take place.
UserService
that class is just a holder for logic. It gets injected a database connection and tests run inside of a transaction that is rolled back. Many would call this bad practice but I found that this works extremely well. Don't need to contort your code just for testing and you get the bug finding power of integration tests.