Field Injection When Mocking Frameworks Fail

Also published on DZone

Challenge

You use Dependency Injection (CDI) in your application and you want to unit test your Java classes without making it an integration test by using Weld of Arquillian. You use a Mocking framework like Mockito or EasyMock but still have trouble getting all your dependencies injected into the class because one or the injections is a String type or another final class.

org.mockito.exceptions.base.MockitoException: Cannot mock/spy class java.lang.String Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types You don’t want to change your code to make it more testable and you don’t want to add accessors to do this. Now you have trouble creating JUnit tests…

Solution

Create your own small injection utility method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void injectField(final Object injectable,
final String fieldname,
final Object value) {
try {
final java.lang.reflect.Field field = injectable.getClass()
.getDeclaredField(fieldname);
final boolean origionalValue = field.isAccessible();
field.setAccessible(true);
field.set(injectable, value);
field.setAccessible(origionalValue);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}

So if you have a class like this you want to test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package nl.ivonet.service.directory;
import nl.ivonet.service.config.Property;
import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.stream.Stream;
public class ExtensionFilter implements DirectoryStream.Filter<Path> {
private static final String DELIMETER = ":";
@Property
@Inject
private String filterExtensions;
@Override
public boolean accept(final Path entry) throws IOException {
return Files.isRegularFile(entry) && isAcceptable(entry);
}
private boolean isAcceptable(final Path entry) {
final String[] split = this.filterExtensions.toLowerCase(Locale.US)
.split(DELIMETER);
return Stream.of(split)
.anyMatch(extension -> entry.getFileName()
.toString()
.toLowerCase(Locale.US)
.endsWith(extension));
}
}

You can test it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package nl.ivonet.service.directory;
import org.junit.Before;
import org.junit.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
import static nl.ivonet.helper.Utils.injectField;
import static org.junit.Assert.assertTrue;
public class ExtensionFilterTest {
private ExtensionFilter filter;
@Before
public void setUp() throws Exception {
this.filter = new ExtensionFilter();
injectField(this.filter, "filterExtensions", ".epub:.kepub");
}
@Test
public void testEpub() throws Exception {
final Path entry = Paths.get("src/test/resources/books/test.epub");
assertTrue(this.filter.accept(entry.toAbsolutePath()));
}
}

For other Injectables you can still use Mockito or your Mocking framework of choice but for final stuff you can do this. Fun and easy.