Mockito使用简介

Mockito使用简介

Mock基础知识

什么是Mock

Mock就是在测试或者开发过程中,对不容易创造或者获取的对象,创造Mock对象来模拟对象的行为。Mock 最大的功能是帮你把单元测试进行解耦,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

Mock的目的

  • 模拟对象方法行为,返回特定值
  • 验证对象某些方法的调用情况

Mock的应用场景

  • 团队并行开发
    有了Mock,前、后端人员只需要定义好接口文档就可以开始并行工作,互不影响。后端与后端之间如果有接口耦合,也同样使用Mock解决。

  • 开启TDD模式
    TDD,即测试驱动开发。单元测试是TDD实现的基石,而TDD经常会碰到协同模块尚未开发完成的情况,但是有了mock,这些一切都不是问题。当接口定义好后,测试人员就可以创建一个Mock,把接口添加到自动化测试环境,提前创建测试。

  • 隔离系统
    假如需要调用一个post请求,为了获得响应,来看当前系统是否能正确处理返回的“响应”,但是这个post请求会造成数据库中数据的污染,那么就可以充分利用Mock,构造一个虚拟的post请求,指定返回的数据就OK。

  • 模拟访问资源
    比如说,你需要调用一个“墙”外的资源来方便自己调试,就可以自己Mock一个。

  • 测试场景
    接口测试:对接口进行测试,排除接口对外部环境的依赖,避免外部环境问题而导致的测试失败。
    功能测试:如果数据准备比较麻烦或者造数据要双方配合成本比较高的项目,在对外部接口进行第一次全面联调测试后,仍然可以考虑使用mock来全面测试自己内部的功能逻辑。
    UI自动化测试:对数据源进行mock,UI自动化测试时,页面上展示的信息是来源于其他的数据源。
    测试覆盖度:例如一个接口有许多种返回类型,有些返回类型不是很容易发生,例如服务器崩溃返回500。这个时候就可以通过mock来模拟返回,达到完整覆盖测试用例的目的。

Mock框架比较

名称简介说明
Mockito消除了对期望行为(expectations)的需要。其它的mocking库需要你在执行前记录期望行为(expectations),而这导致了丑陋的初始化代码。相对 EasyMock 学习成本低,而且具有非常简洁的API,测试代码的可读性很高。
PowerMock这个工具是在EasyMock和Mockito上扩展出来的,目的是为了解决EasyMock和Mockito不能解决的问题,比如对 static, final, private 方法均不能mock。PowerMock 在扩展功能时完全采用和被扩展的框架相同的 API, 熟悉 PowerMock 所支持的模拟框架的开发者会发现 PowerMock 非常容易上手。PowerMock 的目的就是在当前已经被大家所熟悉的接口上通过添加极少的方法和注释来实现额外的功能。
MockMVC基于RESTful风格的SpringMVC单元测试,可以测试完整的SpringMVC流程,即从URL请求到控制处理器,到视图渲染都可以测试。

Mockito使用介绍

环境依赖

<!-- JUnit5 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.7.2</version>
</dependency>
 
<!-- Mockito -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.9.0</version>
</dependency>

常用注解

  1. @ExtendWith(MockitoExtension.class)
    ExtendWith 是 JUnit 5 的注解,该注解可以使用在类或者方法上。表示为使用该注解的类或方法注册扩展功能。@ExtendWith(MockitoExtension.class) 表示为测试类注册 Mockit 测试扩展功能。

    JUnit5 Extensions 详细介绍

  2. @InjectMocks
    使用该注解的类,会实例化一个对象,使用该对象会调用其真实方法。其他使用 @Mock 或 @Spy注解的类会别注入到该实例中。

  3. @Mock
    使用该注解会 mock 一个对象,对该对象的调用不会执行真实方法。

  4. @Spy
    使用该注解会 mock 一个对象,对该对象的调用会执行真实代码,区别于 @Mock
    @InjectMocks@Spy 可以组合进行使用,表示实例化对象,并且可以对该对象方法设置stub。@InjectMocks@Mock 不能组合使用,否则会报错。使用 @InjectMocks 进行对象装配时,需要在类上使用 @ExtendWith(MockitoExtension.class) 注解。或者调用MockitoAnnotations.openMocks(this)手动进行装配。

常用方法

  • Mockito.mock() mock 对象,对该对象调用 不会 执行真实方法
@Test
public void demoTest5() {
    // mock一个List对象
    List list = Mockito.mock(List.class);
    // 调用mock出对象的add()方法
    list.add("first");
    Assertions.assertNull(list.get(0));
}
  • Mockito.spy() mock 对象,对该对象的调用方法 执行真实方法
@Test
public void demoTest6() {
    // mock一个List对象
    ArrayList list = Mockito.spy(ArrayList.class);
    // 调用mock出对象的add()方法
    list.add("first");
    Assertions.assertEquals(list.get(0), "first");
}
  • Mockito.verify() 验证方法调用行为,是否调用,确切调用次数,至少调用的次数
@Test
public void demoTest() {
    // mock一个List对象
    List list = Mockito.mock(List.class);
    
    list.add(1);
    
    list.add(2);
    list.add(2);
 
    list.add(3);
    list.add(3);
    list.add(3);
 
    // 验证调用次数,默认Mockito.times(1)
    Mockito.verify(list).add(1);
    Mockito.verify(list, Mockito.times(2)).add(2);
    Mockito.verify(list, Mockito.times(3)).add(3);
 
    // 方法未调用过,相当于Mockito.times(0)
    Mockito.verify(list, Mockito.never()).add(0);
 
    // 方式至少/最多调用次数
    Mockito.verify(list, Mockito.atMostOnce()).add(1);
    Mockito.verify(list, Mockito.atLeast(2)).add(2);
    Mockito.verify(list, Mockito.atMost(4)).add(3);
 
    // 不符合校验规则会报错
//  Mockito.verify(list, Mockito.times(0)).add(1);
}
  • Mockito.when().thenRuturn() 为测试代码生成 stub
@Test
public void demoTest1() {
    List list = Mockito.mock(List.class);
 
 
    // 生成stubbing
    Mockito.when(list.get(0)).thenReturn("first"); // mock返回数据
    Mockito.when(list.get(1)).thenThrow(new RuntimeException("exception")); // mock抛出异常
 
    Assertions.assertEquals(list.get(0), "first");
    Assertions.assertEquals(Assertions.assertThrows(
        RuntimeException.class, () -> list.get(1)).getMessage(), "exception");
    Assertions.assertEquals(list.get(2), null); // 没有为list.get(2) 生成 stubbing, 返回null
}

@Test
public void demoTest3() {
    List list = Mockito.mock(List.class);
    
    // 会覆盖
    Mockito.when(list.get(0)).thenReturn("first");
    Mockito.when(list.get(0)).thenReturn("second");
    Mockito.when(list.get(0)).thenReturn("third");
    Assertions.assertEquals(list.get(0), "third");
    Assertions.assertEquals(list.get(0), "third");
    Assertions.assertEquals(list.get(0), "third");
 
    Mockito.reset(list);
    
 	Mockito.when(list.get(0)).thenReturn("first").thenReturn("second").thenReturn("third");
    Assertions.assertEquals(list.get(0), "first");
    Assertions.assertEquals(list.get(0), "second");
    Assertions.assertEquals(list.get(0), "third");
}
  • Mockito.reset() 重置与mock对象的交互行为和设置的 stub
@Test
public void demoTest2() {
    List list = Mockito.mock(List.class);
 
    Mockito.when(list.get(0)).thenReturn("first");
    Assertions.assertEquals(list.get(0), "first");
 
    Mockito.reset(list); // 重置
 
    Assertions.assertEquals(list.get(0), null);
}
  • thenCallRealMethod()doCallRealMethod() 调用真实方法
@Test
public void demoTest4() {
    Student student = Mockito.mock(Student.class);
    Assertions.assertEquals(student.studentName(), null);
 
    Mockito.reset(student);
 
    // 调用真实方法
    Mockito.when(student.studentName()).thenCallRealMethod();
//  Mockito.doCallRealMethod().when(student).studentName();
    Assertions.assertEquals(student.studentName(), "zhangsan");
}

常见问题及解决方法

可以根据需要mock方法的多少,选择使用 Mock()Spy()

public class MockitoTestDemo {
    public void goHome() {
        doSomeThingA();
        doSomeThingB();
    }

    // real invoke it.
    public void doSomeThingB() {
        System.out.println("good day");
    }
 
    // auto mock method by mockito
    public void doSomeThingA() {
        System.out.println("you should not see this message.");
        doSomeThingB();
    }
 
    public boolean go() {
        System.out.println("I say go go go!!");
        return true;
    }
 
    @Test
    // 当需要整体Mock,只有少部分方法执行真正部分时,选用这种方式
    public void callRealMethodTest() {
 
        MockitoTestDemo testDemo = Mockito.mock(MockitoTestDemo.class);
         
        Mockito.doCallRealMethod().when(testDemo).goHome();
        Mockito.doCallRealMethod().when(testDemo).doSomeThingB();
 
        testDemo.goHome();
 
        Mockito.verify(testDemo, Mockito.times(1)).doSomeThingA();
        Mockito.verify(testDemo, Mockito.times(1)).doSomeThingB();
    }
 
    @Test
    // 当需要整体执行真正部分,只有少部分方法执行mock,选用这种方式
    public void spyTest() {
        MockitoTestDemo testDemo = Mockito.spy(new MockitoTestDemo());
        // 用thenReturn 会走go()方法体,然后将返回值Mock掉
        Mockito.when(testDemo.go()).thenReturn(false);
        Assertions.assertFalse(testDemo.go());
        // 用doReturn 不走go()方法体
        Mockito.doReturn(false).when(testDemo).go();
        Assertions.assertFalse(testDemo.go());
    }
 
}
  1. ’UnnecessaryStubbingException’ 异常

    org.mockito.exceptions.misusing.UnnecessaryStubbingException: Unnecessary stubbings detected. Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.

    出现以上错误提示的时候,说明出现了未被使用的 stub. Mockito使用 Strict stubbing 特性保证代码的整洁,但是有可能会带来以上错误,这个时候可以去掉这些未被使用的stub。或者使用以下方法:
    Mockito.lenient().when(mock.foo()).thenReturn("ok");
    或者
    Foo mock = Mockito.mock(Foo.class, withSettings().lenient());

  2. 与 MybatisPlus 整合问题

    测试过程中如果遇到 MybatisPlus 提供的, 例如 saveOrUpdate()updateBatchById() 等方法,但是无法跳过的时候。可以使用 @Spy 或者 Mockito.spy() 进行mock 类,并使用 Mockito.doReturn(false).when(service).saveOrUpdate() 生成测试stub。

  3. 出现 ‘MybatisPlusException: can not find lambda cache for this entity’ 错误提示

    使用 MybatisPlus 提供的 LambdaWrapper 的时候会使用到Entity的一些缓存起来的类相关信息,找不到便会报错。
    TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), PrdNoticeMachineEntity.class);,可以手动通过添加以上代码解决该问题。


参考文档

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mockito是一个用于Java的开源测试框架,用于创建和管理模拟对象(mock objects)。它可以帮助我们进行单元测试,特别是在测试依赖对象时非常有用。 在Mockito中,我们可以使用注解来简化模拟对象的创建。例如,使用`@Mock`注解可以创建一个模拟对象,使用`@InjectMocks`注解可以将模拟对象注入到被测试对象中。 Mockito还提供了一些方法来验证模拟对象的交互和行为。例如,使用`verify`方法可以验证方法是否被调用,使用`times`方法可以指定方法被调用的次数,使用`never`方法可以验证方法是否从未被调用。 另外,Mockito还支持设置模拟对象的行为。我们可以使用`when`方法来设置模拟对象方法的返回值,使用`doReturn`方法来设置模拟对象方法的行为。 总之,Mockito是一个强大的测试框架,可以帮助我们进行单元测试,并且使用注解可以简化模拟对象的创建。通过验证和设置模拟对象的行为,我们可以更好地测试我们的代码。 #### 引用[.reference_title] - *1* *2* *3* [【码农教程】手把手教你Mockito使用](https://blog.csdn.net/AI_Green/article/details/129163693)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值