IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> Mockito使用简介 -> 正文阅读

[开发测试]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);,可以手动通过添加以上代码解决该问题。


参考文档

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-12-26 22:30:16  更:2021-12-26 22:32:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 4:22:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码