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 @InjectMocks annotations allow us to inject mocked dependencies in the annotated class mocked object. This is useful when we have external dependencies in the class we want to mock. We can specify the mock objects to be injected using @Mock or @Spy annotations.
@InjectMocks creates an instance of the class and injects the mocks that are created with the @Mock annotations into this instance. @Mock is used to create mocks that are needed to support the testing of the class to be tested. @InjectMocks is used to create class instances that need to be tested in the test class.
Alternative when mockitos build in functions do not suffice: Try needle4j.org
It's a injection/mock framework that allows injection of mocks and concrete instances and also supports postConstruct for lifecycle simulation.
public class ProductionCodeClassTest {
@Rule
public final NeedleRule needle = new NeedleRule();
// will create productionCodeClass and inject mocks by default
@ObjectUnderTest(postConstruct=true)
private ProductionCodeClass testedInstance;
// this will automatically be a mock
@Inject
private AServiceProductionCodeClassDependsOn serviceMock;
// this will be injected into ObjectUnderTest
@InjectIntoMany
private ThisIsAnotherDependencyOfProdcutionCodeClass realObject = new ThisIsAnotherDependencyOfProdcutionCodeClass ();
@Test
public void test_stuff() {
....
}
}
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With