Java – Mockito – Mock generic objects of the same type

javajunitmockingmockitounit testing

I am trying to mock my GenericDao objects, using Mockito Framework 1.9.5 and JUnit 4.11, but Mockito does always mock the first field, which matches the type. Also qualifying with the name does not help.

Like described in the API (http://docs.mockito.googlecode.com/hg-history/58d750bb5b94b6e5a554190315811f746b67f578/1.9.5/org/mockito/InjectMocks.html), Mockito should evaluate the correct field to mock.

Expected output:

EmployeeService.absenceDao -> null
EmployeeService.creditDao -> null
EmployeeService.employeeDao -> Mocked object

Effective output:

EmployeeService.absenceDao -> Mocked object
EmployeeService.creditDao -> null
EmployeeService.employeeDao -> nulll

Code to reproduce the situation:

@RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceTest {

    @InjectMocks
    EmployeeService employeeService;

    @Mock(name = "employeeDao")
    GenericDao<Employee> employeeDao;

    @Test
    public void testFindEmployeeByUsername() {
        // some tests
    }
}

My class to mock, contains several GenericDao fields but I just want to mock the employeeDao:

@Service
@Transactional
public class EmployeeService {
    @Autowired
    private GenericDao<Employee> employeeDao;
    @Autowired
    private GenericDao<Credit> creditDao;
    @Autowired
    private GenericDao<Absence> absenceDao;

Best Answer

From what I have debugged it looks like Mockito can't handle type parameters through annotations. If you follow the stacktrace of MockitoJunitRunner you eventually get into the DefaultAnnotationEngine below is the method you want to look at.

public void process(Class<?> clazz, Object testInstance) {
        Field[] fields = clazz.getDeclaredFields(); //This is the line of concern
        for (Field field : fields) {
            boolean alreadyAssigned = false;
            for(Annotation annotation : field.getAnnotations()) {           
                Object mock = createMockFor(annotation, field);
                if (mock != null) {
                    throwIfAlreadyAssigned(field, alreadyAssigned);                    
                    alreadyAssigned = true;                    
                    try {
                        new FieldSetter(testInstance, field).set(mock);
                    } catch (Exception e) {
                        throw new MockitoException("Problems setting field " + field.getName() + " annotated with "
                                + annotation, e);
                    }
                }        
            }
        }

The line I commented pulls back your GenericDao without the type parameter. So then when it goes to inject it. Now it shouldn't really matter because you are normally mocking that Object anyway controlling what is being returned. But in your case you have 3 dao's which means when you make that call to your employeeDao and the mock is on AbsenceDao it will throw a NullPointerException. I did infact try it :). But if you mock all three. It passes. Now I was concerned that it actually called the right method, so I wrote some verify cases to make sure. Below is what I did.

GenericDAO

public interface GenericDao<T>
{
    public T getObject();
}

EmployeeService

@Service
@Transactional
public class EmployeeService
{
    @Autowired
    private GenericDao<Absence>  absenceDao;
    @Autowired
    private GenericDao<Credit>   creditDao;
    @Autowired
    private GenericDao<Employee> employeeDao;


    public Absence getAbsenceObject()
    {
        return absenceDao.getObject();
    }

    public Credit getCreditObject()
    {
        return creditDao.getObject();
    }

    public Employee getEmployeeObject()
    {
        return employeeDao.getObject();
    }
}

EmployeeServiceTest

@RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceTest
{
    @Mock
    private GenericDao<Absence>  absenceDao;
    @Mock
    private GenericDao<Credit>   creditDao;
    @Mock
    private GenericDao<Employee> employeeDao;
    @InjectMocks
    private EmployeeService      employeeService;

    @Test
    public void testGetObject()
    {
        Mockito.when(absenceDao.getObject()).thenReturn(new Absence());
        Mockito.when(creditDao.getObject()).thenReturn(new Credit());
        Mockito.when(employeeDao.getObject()).thenReturn(new Employee());

        Assert.assertNotNull(employeeService.getEmployeeObject());

        Mockito.verify(absenceDao, Mockito.never()).getObject();
        Mockito.verify(creditDao, Mockito.never()).getObject();
        Mockito.verify(employeeDao, Mockito.times(1)).getObject();
    }
}

I hope this helps. Bravo I learned something as well answering this.