Mockito 中模拟对象的自动装配字段 returns null
Autowired fields of mocked object returns null in Mockito
我有一个StudentService
class。并且我写了一个class用于StudentService
class的单元测试方法。我的代码如下:-
@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();
}
}
并且在我的单元测试中class,我写了下面的方法。
@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);
}
当我执行测试 class 时,出现错误。这是因为在StudentService
class中,在getPresentStudentCount()
方法中,在第8行,studentInstitutionMapper
字段是null
。这只发生在模拟对象上。我如何获取模拟对象的自动装配字段?
在你的测试 class 中尝试像这样声明对象 studentInstitutionMapper
。
@Mock
StudentInstitutionMapper studentInstitutionMapper;
您可以使用@Mock 注释注入自动装配class。在许多情况下,您应该使用 @InjectMocks 注释创建测试 class 实例,多亏了这个注释,您的模拟可以直接注入。
@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);
}
这有助于更好地解释:Difference between @Mock and @InjectMocks
您需要在单元测试中使用@InjectMocks 注释:
@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);
}
}
您还应该在单元测试中配置 studentInstitutionMapper 的行为,使其return达到预期的结果。
有一种简单的解决方案不涉及 mockito 的高级注释:
您可以像这样重构 StudentService
:
public class StudentService {
private final StudentInstitutionMapper studentInstitutionMapper;
public StudentService(StudentInstitutionMapper studentInstitutionMapper) {
this.studentInstitutionMapper = studentInstitutionMapper;
}
}
这个解决方案是我最喜欢的解决方案,因为当我在测试中创建 StudentService
时,我确切地看到它需要什么依赖项,它们的类型。因此,即使不打开 StudentService
class.
的源代码,我也可以提供 mocks/real 实现
这种类型的注入(构造函数注入,而不是您在问题中使用的字段注入)的另一个好处是,不会破坏字段的封装。
备注:
我没有把 @Autowired
放在构造函数上,因为在 spring 的最新版本中只要有一个构造函数就不需要它(对于单元测试它是无关紧要的完全没有)。
如果您担心构造函数的样板代码,您可以使用 Lombok 并放置一个注释来为您生成全参数构造函数。结合注释 1,这允许完全删除构造函数代码
P.S。我不打算在这里开始字段注入与构造函数注入的 "holy-war",我只是在说明这种方法,因为之前没有人在其他答案中提到过它,从技术上讲它解决了问题中提出的问题。欢迎google讨论这个话题。
我有一个StudentService
class。并且我写了一个class用于StudentService
class的单元测试方法。我的代码如下:-
@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();
}
}
并且在我的单元测试中class,我写了下面的方法。
@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);
}
当我执行测试 class 时,出现错误。这是因为在StudentService
class中,在getPresentStudentCount()
方法中,在第8行,studentInstitutionMapper
字段是null
。这只发生在模拟对象上。我如何获取模拟对象的自动装配字段?
在你的测试 class 中尝试像这样声明对象 studentInstitutionMapper
。
@Mock
StudentInstitutionMapper studentInstitutionMapper;
您可以使用@Mock 注释注入自动装配class。在许多情况下,您应该使用 @InjectMocks 注释创建测试 class 实例,多亏了这个注释,您的模拟可以直接注入。
@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);
}
这有助于更好地解释:Difference between @Mock and @InjectMocks
您需要在单元测试中使用@InjectMocks 注释:
@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);
}
}
您还应该在单元测试中配置 studentInstitutionMapper 的行为,使其return达到预期的结果。
有一种简单的解决方案不涉及 mockito 的高级注释:
您可以像这样重构 StudentService
:
public class StudentService {
private final StudentInstitutionMapper studentInstitutionMapper;
public StudentService(StudentInstitutionMapper studentInstitutionMapper) {
this.studentInstitutionMapper = studentInstitutionMapper;
}
}
这个解决方案是我最喜欢的解决方案,因为当我在测试中创建 StudentService
时,我确切地看到它需要什么依赖项,它们的类型。因此,即使不打开 StudentService
class.
这种类型的注入(构造函数注入,而不是您在问题中使用的字段注入)的另一个好处是,不会破坏字段的封装。
备注:
我没有把
@Autowired
放在构造函数上,因为在 spring 的最新版本中只要有一个构造函数就不需要它(对于单元测试它是无关紧要的完全没有)。如果您担心构造函数的样板代码,您可以使用 Lombok 并放置一个注释来为您生成全参数构造函数。结合注释 1,这允许完全删除构造函数代码
P.S。我不打算在这里开始字段注入与构造函数注入的 "holy-war",我只是在说明这种方法,因为之前没有人在其他答案中提到过它,从技术上讲它解决了问题中提出的问题。欢迎google讨论这个话题。