I am programming in a Java SE environment using WELD-SE for dependency injection. Therefore dependencies of a class look something like this:
public class ProductionCodeClass {
@Inject
private DependencyClass dependency;
}
When writing a unit test for this class I am creating a mock for DependencyClass
and as I don't want to start a complete CDI environment for every test I run, I "inject" the mock manually:
import static TestSupport.setField;
import static org.mockito.Mockito.*;
public class ProductionCodeClassTest {
@Before
public void setUp() {
mockedDependency = mock(DependencyClass.class);
testedInstance = new ProductionCodeClass();
setField(testedInstance, "dependency", mockedDependency);
}
}
The statically imported method setField()
I have written myself in a class with tools I use in testing:
public class TestSupport {
public static void setField(
final Object instance,
final String field,
final Object value) {
try {
for (Class classIterator = instance.getClass();
classIterator != null;
classIterator = classIterator.getSuperclass()) {
try {
final Field declaredField =
classIterator.getDeclaredField(field);
declaredField.setAccessible(true);
declaredField.set(instance, value);
return;
} catch (final NoSuchFieldException nsfe) {
// ignored, we'll try the parent
}
}
throw new NoSuchFieldException(
String.format(
"Field '%s' not found in %s",
field,
instance));
} catch (final RuntimeException re) {
throw re;
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
}
What I don't like about this solution is, that I need this helper over and over in any new project. I already packaged it as a Maven project I can add as a test dependency to my projects.
But isn't there something ready made in some other common library I am missing? Any comments on my way of doing this in general?
Mockito supports this out of the box:
public class ProductionCodeClassTest {
@Mock
private DependencyClass dependency;
@InjectMocks
private ProductionCodeClass testedInstance;
@Before
public void setUp() {
testedInstance = new ProductionCodeClass();
MockitoAnnotations.initMocks(this);
}
}
The @InjectMocks
annotation will trigger injection of classes or interfaces mocked in the test class, in this case DependencyClass
:
Mockito tries to inject by type (using name in case types are the same). Mockito does not throw anything when injection fails - you will have to satisfy the dependencies manually.
Here, I am also using the @Mock
annotation instead of calling mock()
. You could still use mock()
, but I prefer using annotations.
As a side note, there are reflection tools available, which supports the functionality you implemented in TestSupport
. One such example is ReflectionTestUtils
.
Perhaps better still is to use constructor injection:
public class ProductionCodeClass {
private final DependencyClass dependency;
@Inject
public ProductionCodeClass(DependencyClass dependency) {
this.dependency = dependency;
}
}
The main advantage here is that it is clear what classes the class depends on, and that it cannot easily be constructed without providing all the dependencies. Also, it allows the injected class to be final.
By doing this, @InjectMocks
is not necessary. Instead, just create the class by providing the mock as a parameter to the constructor:
public class ProductionCodeClassTest {
@Mock
private DependencyClass dependency;
private ProductionCodeClass testedInstance;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
testedInstance = new ProductionCodeClass(dependency);
}
}