Mockito InjectMocks字段无法注入其他InjectMocks字段的解决办法

来源:互联网 发布:ubuntu mate 树莓派 编辑:程序博客网 时间:2024/06/08 08:49

今天写单元测试用例,跑起来后,出现了空指针异常。于是查了下,发现Mock对象的一个属性未注入,为null。我的程序结构大致为:

@Repositorypublic class MyRepository {    public void doSomething() {        System.out.println("here's dosomething");    }    public Model findById(Long id) {        return new Model(id, "Real Repository");    }}

@Servicepublic class MyService {    @Autowired    private MyRepository myRepository;    public void doSomething() {        this.myRepository.doSomething();    }    public Model findById(Long id) {        return this.myRepository.findById(id);    }}

@Controllerpublic class MyController {    @Autowired    private MyService myService;    public void doSomething() {        this.myService.doSomething();    }    public Model findById(Long id) {        return this.myService.findById(id);    }}

@RunWith(MockitoJUnitRunner.class)public class MyControllerTest {    @Mock    private MyRepository myRepository;    @InjectMocks    private MyService myService;    @InjectMocks    private MyController myController;    @Before    public void setUp() throws Exception {        Model model = new Model(11L, "AAA");        doNothing().when(myRepository).doSomething();        when(myRepository.findById(11L)).thenReturn(model);    }    @Test    public void doSomething() throws Exception {        this.myController.doSomething();    }    @Test    public void findById() throws Exception {        System.out.println(this.myController.findById(11L));    }}

使用Mock打桩的为MyRepository,原本以为使用InjectMocks后,MyService会自动注入MyRepository,MyController会自动注入前的MyService,但是结果并不是这样的。MyController认不到MyService。MyController实例后,没有给myService属性赋值。看起来InjectMocks只能使用Mock注解的。于是想在MyService上加个Mock,虽然编译没问题,但是运行起来异常了:

org.mockito.exceptions.base.MockitoException: This combination of annotations is not permitted on a single field:@Mock and @InjectMocks
于是只能另寻他路,考虑到使用Spring来做容器管理,修改Test类:

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = {"classpath:beans.xml"})public class MyControllerTest {    @Mock    private MyRepository myRepository;    @InjectMocks    @Autowired    private MyService myService;    @Autowired    private MyController myController;    @Before    public void setUp() throws Exception {        MockitoAnnotations.initMocks(this);        Model model = new Model(11L, "AAA");        doNothing().when(myRepository).doSomething();        when(myRepository.findById(11L)).thenReturn(model);    }    @Test    public void doSomething() throws Exception {        this.myController.doSomething();    }    @Test    public void findById() throws Exception {        System.out.println(this.myController.findById(11L));    }}
果然可以了,模拟了数据库访问:


@autowire加哪里还有讲究,必须2个都加,否则也会出现注入不到的情况。

既然没法注入,其实不借助容器,也可以手动来赋值。在setup方法中做下修改,便得到方法二:

@RunWith(MockitoJUnitRunner.class)public class MyControllerTest {    @Mock    private MyRepository myRepository;    @InjectMocks    private MyService myService;    @InjectMocks    private MyController myController;    @Before    public void setUp() throws Exception {        ReflectionTestUtils.setField(myController, "myService", myService);        Model model = new Model(11L, "AAA");        doNothing().when(myRepository).doSomething();        when(myRepository.findById(11L)).thenReturn(model);    }    @Test    public void doSomething() throws Exception {        this.myController.doSomething();    }    @Test    public void findById() throws Exception {        System.out.println(this.myController.findById(11L));    }}

执行效果是一样的,如上图。

两种方法都能满足多级的注入情形。如果不想依赖Spring,可以选择用第2种的方式。

原创粉丝点击