Mockito

发布时间 2023-12-06 14:42:11作者: 嘿嘿-

Mockito

1.简介

Mockito 是一种 Java Mock 框架,主要是用来做 Mock 测试,
它可以模拟任何 Spring 管理的 Bean、模拟方法的返回值、模拟抛出异常等等

用于快速创建和配置mock对象。通过创建外部依赖的 Mock 对象, 然后将此 Mock 对象注入到测试类中,简化有外部依赖的类的测试。

我们注重于方法内的逻辑测试即可

官网:https://site.mockito.org/

2.概念介绍

常用注解

Mockito框架中包含了一些常用的注解,下面是对这些注解的详细解释:

  1. @Mock:用于创建一个Mock对象。这个注解创建的Mock对象不会自动注入到Spring容器中。

  2. @MockBean:用于创建一个Mock对象,并将其添加到Spring容器中。这个注解通常用于Spring Boot的集成测试中。

  3. @InjectMocks:用于创建一个实例,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。

  4. . @Spy:用于创建一个Spy对象。Spy对象是一种特殊的Mock对象,它默认会调用真实的方法。

  5. @Captor:用于创建一个参数捕获器。参数捕获器可以用来捕获方法调用的参数值。

  6. @BeforeEach:标记在方法上,表示该方法应该在每个测试方法之前执行。

  7. @AfterEach:标记在方法上,表示该方法应该在每个测试方法之后执行。

  8. @BeforeAll:标记在方法上,表示该方法应该在所有测试方法之前执行一次。

  9. @AfterAll:标记在方法上,表示该方法应该在所有测试方法之后执行一次。

3.mockito使用

注意事项

需要结合Junit使用(普通的Spring项目)

springboot项目的话,boot-test包默认已经引入mockito相关

前期准备

普通的spring项目,倒入相关jar包,

需要结合junit使用

     <dependency>
          <groupId>org.mockito</groupId>
          <artifactId>mockito-core</artifactId>
          <scope>test</scope>
      </dependency>

     <!--下面几个非强制。根据实际情况决定是否添加-->
      <!--mockito模拟static方法需要引⼊-->
      <dependency>
          <groupId>org.mockito</groupId>
          <artifactId>mockito-inline</artifactId>
          <scope>test</scope>
      </dependency>
      <!--mockito断言匹配器-->
      <dependency>
          <groupId>org.hamcrest</groupId>
          <artifactId>hamcrest-core</artifactId>
          <scope>test</scope>
      </dependency>
      <!--mockito流式断言,比junit自带的assert更强⼤,更不容易出错-->
      <dependency>
          <groupId>org.assertj</groupId>
          <artifactId>assertj-core</artifactId>
          <scope>test</scope>
      </dependency>

springBoot项目的话就无需这些,starter-test包里自带

结合Spring项目使用

基本使用

/**
 * @author xwp
 * @date 2023/12/5 14:25
 */
@RunWith(MockitoJUnitRunner.class)
public class DeviceFacadeImplTest {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @InjectMocks 
    private DeviceFacadeImpl deviceFacade;
    @Mock
    private IEmMemberDeviceService emMemberDeviceService;
 

    @Before
    public void setUp() throws Exception {
      
    }


    /**
     * 测试--正常流程
     */
    @Test
    public void testOpenAutoStreaming() {
        //构建目标方法的请求参数--com.fzs.server.service.facade.DeviceFacade.openAutoStreaming
        OpenStreamingParam openStreamingParam = new OpenStreamingParam();
        openStreamingParam.setUserId("1");
        openStreamingParam.setStreamType(1);
        openStreamingParam.setInstanceId("1");
        String memberId = "1";


        //制造条件  mock其他服务返回数据
        EmMemberDevice emMemberDevice = new EmMemberDevice();
        emMemberDevice.setDeviceRunningStatus(1);
        emMemberDevice.setStatus(50);
        emMemberDevice.setFrozenStatus(0);
        emMemberDevice.setDeviceId("1");
        emMemberDevice.setPhoneId("1");
        when(emMemberDeviceService.getOne(any())).thenReturn(emMemberDevice);



        //测试调用目标方法
        OpenAutoStreamResult openAutoStreamResult = deviceFacade.openAutoStreaming(memberId, openStreamingParam);
        logger.info("返回结果,{}", openAutoStreamResult);
    }

}

4.常见场景

1.如何mock@Value引入的属性

可以用反射+@Before

@Before:在目标方法执行前会调用(在junit 5.x中,@Before主键被@BeforeEach所替代。)

@Before
  public void setUp() throws Exception {
      //mock设置@Value的值 (反射)
      Field field = DeviceFacadeImpl.class.getDeclaredField("enableADN");
      field.setAccessible(true);
      field.set(deviceFacade, false);
  }

2.返回void的方法mock?

doNothing

doNothing().when(stringValueOperations).set(any(String.class), any(String.class));

3.如何mock redisTemplate的set/get方法

在@Before里面mock valueOperations对象,然后在@Test方法mock调用其set/get

@Mock
RedisTemplate<String, Object> redisTemplate;
@Mock
private ValueOperations<String, Object> valueOperations;

@Before
public void setUp() throws Exception {
	when(redisTemplate.opsForValue()).thenReturn(valueOperations);
}

@test1
public void test1() throws Exception {
	when(valueOperations.get(any())).thenReturn(1);
  doNothing().when(valueOperations).set(any(String.class), any(Object.class), any(long.class), any(TimeUnit.class));
}

4.mybatisPlus在使用Lambda表达式处理查询问题?

如果在mybatisPlus使用了lambda表达式的查询的话,调用mock对象emDevicesStreamsService的getOne方法,例如:

         EmDevicesStreams ds = emDevicesStreamsService.getOne(new QueryWrapper<EmDevicesStreams>().lambda().eq(EmDevicesStreams::getDeviceId, deviceId).orderByDesc(EmDevicesStreams::getId).last(" LIMIT 1 "));

会报错:

can not find lambda cache for this entity [com.fzs.server.service.entity.EmDevicesStreams

这个问题是由于MyBatis Plus在处理Lambda表达式时,需要获取实体类的元数据,但在Mock环境中,由于类加载器的问题,可能无法正确获取这些元数据,从而导致这个错误。

解决:

在@Before里面添加:

 TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), EmDevicesStreams.class);

具体原因分析参考:

https://blog.csdn.net/qq_36491545/article/details/109091325