@Mock @MockBean @InjectMocks之间的关系

首先说@mock 和 @MockBean
具体的介绍大家可以看
Mockito的@Mock与@MockBean

一言以蔽之就是
@Mock可以生产一个空的类,这个类的方法体都是空的,方法的返回值(如果有的话)都是null。
@MockBean可以生产一个空的类,并且用这个类替代spring容器中同类型的类。

而@InjectMocks就是产生一个空的类,这个类里面的字段用这个测试类里面被@Mock的字段填充。

@SpringBootTest
public class MockitoTest {

    @MockBean
    private MyService myService;

    @InjectMocks
    private  UserController userController;
 @Test
    public void testGet(){


        Person person = new Person();
        person.setName("张三");
        Long id = 15L;
        when(personMapper.selectById(id)).thenReturn(person);
        when(myService.say()).thenReturn("by mock");
        Person result = userController.getUserById(id);

    }

我写了上面的测试代码,发现不论怎么运行,userController里面的MyService 字段都是null,感觉就是userController里面MyService 字段上面的autowired没有起作用。
我一直在调整代码,发现了如下的情况

  • 前提条件
    ClassB里面有个ClassA,classa被标注了Autowired

  • case1
    测试类上只有 @ExtendWith(MockitoExtension.class) 的情况下 测试类里被 @MockBean 标注的字段就是空的

  • case2
    测试类上只有 @ExtendWith(SpringExtension.class)的情况下
    测试类里被 @MockBean 标注的字段(假定为ClassA)不是空的
    但是被 @InjectMocks标注的类(假定为ClassB)
    ClassB里面标注了Autowired的ClassA依然是null

  • case3
    @SpringBootTest 里面本身就包含着 @ExtendWith(SpringExtension.class)
    如果测试类上只有 @SpringBootTest 测试类里被 @MockBean 标注的字段(假定为ClassA)不是空的
    但是被 @InjectMocks标注的类(假定为ClassB)
    ClassB里面标注了Autowired的ClassA依然是null

  • case4
    如果测试类上只有 @ExtendWith(MockitoExtension.class)
    测试类里被 @Mock标注的字段不是null,也能正常的注入被 @InjectMocks标注的类

  • case5
    如果测试类上只有 @SpringBootTest
    虽然测试类里classB被标注了 @InjectMocks
    但是classB里面的classA依然是null 即使代码里面的ClassA类上标注了 @Component

看到我头都大了。
我的目标就是把 @MockBean标注的类注入到@InjectMocks里面。但是一直不行。

最终我把@InjectMocks改成了@autowired 发现可以注入了
但是被标注了@autowired里面缺不能注入被标注了@mock的字段!

最终得到一个结论
被@InjectMocks标注的字段,只能被注入被@mock标注的类(@mockbean标注的不行)
被@autowired标注的字段,只能被注入被@mockbean标注的类(@mock标注的不行)

再简而言之就是
要想把需要打桩的类注入到我们要测试的类里面,有下面两个思路

  • @ExtendWith(MockitoExtension.class) + @InjectMocks +@Mock
  • @SpringBootTest +@Autowired+@MockBean
    两个的区别在于
    • 是否启动了SpringBoot。(当然这个区别没有现实意义,因为我写代码做测试的目的是验证模块功能的正确性,是否启动Spirngboot只是手段不是目的)
    • 启动SpringBoot方便做集成测试,使用ExtendWith(MockitoExtension.class)方便做模块内简单的单元测试。(至于这两种测试的区别,google上的说明有很多,不过我同时的认为前者测试的范围更广,粒度也更大,依赖的模块更多,依赖也要求是真实实现的)

参考资料