1

I have a StudentService class. And I have written a class for unit testing methods of StudentService class. My code is as follows:-

@Component
@EnableAutoConfiguration
public class StudentService {

@Autowired
StudentInstitutionMapper studentInstitutionMapper;

public Integer getPresentStudentCount(StudentParams studentParam) {
    // TODO Auto-generated method stub
    StudentInstitutionExample example = new StudentInstitutionExample();
    StudentInstitutionExample.Criteria criteria = example.createCriteria();
    criteria.andActiveYnEqualTo("Y");
    criteria.andDeleteYnEqualTo("N");
    criteria.andIsPresentEqualTo("Y");
    criteria.andInstitutionTestIdEqualTo(studentParam.getInstitutionTestId());
    List<StudentInstitution> studentInstitutionList = studentInstitutionMapper.selectByExample(example);//line 8 in method
    return studentInstitutionList.size();
}


}

And in my unit testing class, I have written following method.

    @Test
    public void testStudentService_getPresentStudentCount1()
    {



        StudentService service=new StudentService();
        StudentParams studentParam=mock(StudentParams.class);

        Integer institutionTestId=3539;
        when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId);


        int i=service.getPresentStudentCount(studentParam);
        assertEquals(0,i);

    }

when I execute the test class, i get error. This is because in StudentService class, in getPresentStudentCount() method, at line 8, the studentInstitutionMapper field is null. This is happening only for mocked object. How do i get autowired fields of mocked object?

Ihor Patsian
  • 1,288
  • 2
  • 15
  • 25
Karthik Pai
  • 587
  • 1
  • 9
  • 18

4 Answers4

2

Try declaring the object studentInstitutionMapper like this in your test class.

@Mock
StudentInstitutionMapper studentInstitutionMapper;
Galbi
  • 190
  • 1
  • 11
1

You can inject autowired class with @Mock annotation. In many case you should create your test class instance with @InjectMocks annotation, thanks to this annotation your mocks can inject directly.

@RunWith(PowerMockRunner.class)
@PrepareForTest({StudentService.class})
public class StudentServiceTest {

@InjectMocks
StudentService service;

@Mock
StudentInstitutionMapper studentInstitutionMapper;

@Test
public void testStudentService_getPresentStudentCount1()
{
    MockitoAnnotations.initMocks(this);

    StudentParams studentParam=mock(StudentParams.class);

    Integer institutionTestId=3539;
    when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId);


    int i=service.getPresentStudentCount(studentParam);
    assertEquals(0,i);


}

This would be help for better explanation: Difference between @Mock and @InjectMocks

oguzhan00
  • 499
  • 8
  • 16
1

There is one simple solution that doesn't not involve advanced annotations of mockito:

You can refactor the StudentService like this:

public class StudentService {

    private final StudentInstitutionMapper studentInstitutionMapper;

    public StudentService(StudentInstitutionMapper studentInstitutionMapper) {
       this.studentInstitutionMapper = studentInstitutionMapper;
    }
}

This solution is my favorite one because when I create the StudentService in test I see exactly what dependencies it requires, their type. So I can supply mocks/real implementation even without opening the source of StudentService class.

Another benefit of this type of injection (constructor injection as opposed to field injection that you've used in the question) is that nothing breaks encapsulation of the fields.

Notes:

  1. I didn't put @Autowired on constructor because in recent version of spring its not required as long as there is a single constructor (and for unit tests its irrelevant at all).

  2. If you're concerned about boilerplate code of constructor you can use Lombok and put an annotation for generating the all-args constructor for you. In conjunction with Note 1 this allows to drop the constructor code altogether

P.S. I do not intend to start the "holy-war" of field injection vs constructor injection here, I'm just stating this approach because no-one has mentioned it before in other answers and technically it solves the issue raised in the question. Feel free to google about this topic.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
0

You need to use the @InjectMocks annotation in your unit test :

@ExtendWith(MockitoExtension.class)
class StudentServiceTest {

    @Mock
    private StudentInstitutionMapper studentInstitutionMapper;

    @InjectMocks
    private StudentService studentService;

    @Test
    public void testStudentService_getPresentStudentCount1() {


        StudentParams studentParam = mock(StudentParams.class);

        Integer institutionTestId = 3539;
        when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId);


        int i = studentService.getPresentStudentCount(studentParam);
        assertEquals(0, i);

    }
}

You should also configure the behavior of the studentInstitutionMapper in the unit test to make it return the expected result.

Fabien
  • 346
  • 2
  • 10
  • This is also a great solution. But as I am using JUnit4, the ticked answer worked for me. Thanks. – Karthik Pai Nov 21 '19 at 07:55
  • For junit 4 you can replace `@ExtendWith(MockitoExtension.class)` by `@RunWith(MockitoJUnitRunner.class)` The ticked answer uses powerMock, that doesn't seem useful in your case. The call to `MockitoAnnotations.initMocks(this);`isn't necessary. – Fabien Nov 21 '19 at 11:06
  • Hi Fabien!! Thanks for your help. I tried this answer. As you said I also replaced the annotation. I am getting one problem. I am not able to import @InjectMock. Is there any spelling mistake. Is it @InjectMocks? – Karthik Pai Nov 22 '19 at 05:15
  • Thanks for your help. This saved me a lot – Karthik Pai Nov 22 '19 at 07:29