How to inject mocks while testing classes using CDI in production

Matthias Wimmer picture Matthias Wimmer · Sep 6, 2015 · Viewed 14.6k times · Source

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?

Answer

Magnilex picture Magnilex · Sep 6, 2015

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);
    }

}