【问题标题】:Autowired fields of mocked object returns null in Mockito模拟对象的自动装配字段在 Mockito 中返回 null
【发布时间】:2019-11-15 12:39:45
【问题描述】:

我有一个StudentService 课程。我已经为StudentService 类的单元测试方法编写了一个类。我的代码如下:-

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


}

在我的单元测试课中,我编写了以下方法。

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

    }

当我执行测试类时,我得到了错误。这是因为在StudentService 类中,在getPresentStudentCount() 方法中,在第8 行,studentInstitutionMapper 字段是null。这只发生在模拟对象上。如何获取模拟对象的自动装配字段?

【问题讨论】:

    标签: java unit-testing mockito junit4


    【解决方案1】:

    尝试在您的测试类中像这样声明对象studentInstitutionMapper

    @Mock
    StudentInstitutionMapper studentInstitutionMapper;
    

    【讨论】:

      【解决方案2】:

      您可以使用@Mock 注解注入自动装配类。在许多情况下,您应该使用 @InjectMocks 注释创建您的测试类实例,多亏了这个注释,您的模拟可以直接注入。

      @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

      【讨论】:

        【解决方案3】:

        有一个简单的解决方案,不涉及 mockito 的高级注释:

        您可以像这样重构StudentService

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

        这个解决方案是我最喜欢的一个,因为当我在测试中创建StudentService 时,我可以准确地看到它需要什么依赖项,它们的类型。因此,即使不打开StudentService 类的源代码,我也可以提供模拟/真实实现。

        这种类型的注入(构造函数注入,而不是您在问题中使用的字段注入)的另一个好处是不会破坏字段的封装。

        注意事项:

        1. 我没有将 @Autowired 放在构造函数上,因为在最新版本的 spring 中,只要有一个构造函数就不需要它(对于单元测试,它根本不相关)。

          李>
        2. 如果您担心构造函数的样板代码,您可以使用 Lombok 并添加注释来为您生成全参数构造函数。结合注 1,这允许完全删除构造函数代码

        附:我不打算在这里开始字段注入与构造函数注入的“圣战”,我只是在说明这种方法,因为之前没有人在其他答案中提到过它,并且从技术上讲它解决了问题中提出的问题。随意在谷歌上搜索这个主题。

        【讨论】:

          【解决方案4】:

          您需要在单元测试中使用@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 的行为,使其返回预期的结果。

          【讨论】:

          • 这也是一个很好的解决方案。但是当我使用 JUnit4 时,勾选的答案对我有用。谢谢。
          • 对于junit 4,您可以将@ExtendWith(MockitoExtension.class) 替换为@RunWith(MockitoJUnitRunner.class) 勾选的答案使用powerMock,这在您的情况下似乎没有用。不需要调用MockitoAnnotations.initMocks(this);
          • 嗨法比恩!谢谢你的帮助。我试过这个答案。正如你所说,我也替换了注释。我遇到了一个问题。我无法导入@InjectMock。有没有拼写错误。是@InjectMocks 吗?
          • 感谢您的帮助。这为我节省了很多
          猜你喜欢
          • 1970-01-01
          • 2014-06-03
          • 2020-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-05
          • 1970-01-01
          • 2021-09-02
          相关资源
          最近更新 更多