java单元测试之mock篇

  • 一、什么是mock?
  • 二、为什么要进行mock?
  • 三、IDEA中使用Mock
    • 3.1、引入mock所需依赖
    • 3.1、IDEA单元测试必备快捷键
    • 3.2、Mock测试相关注解
      • @Mock注解
      • @InjectMocks注解
      • 调用PowerMockito.spy()方法
      • Mock使用方式或者技巧
      • 静态方法mockStatic
      • @PrepareForTest的使用场景
      • 阻止代码初始化,包括static{}静态代码块或者static变量的初始化
    • 3.3、问题

一、什么是mock?

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取的比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

二、为什么要进行mock?

Mock是为了解决不同的单元之间由于耦合而难于开发、测试的问题。所以,Mock既能出现在单元测试中,也会出现在集成测试、系统测试过程中。Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
比如一段代码有这样的依赖:

当我们需要测试A类的时候,如果没有 Mock,则我们需要把整个依赖树都构建出来,而使用 Mock 的话就可以将结构分解开,像下面这样:

三、IDEA中使用Mock

3.1、引入mock所需依赖

 <dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.9.0</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-core</artifactId><version>2.0.9</version></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.9</version></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.9</version><scope>test</scope></dependency>

3.1、IDEA单元测试必备快捷键

新建UT——Ctrl + Shift + T
双击所要进行单元测试的类,点击Ctrl + Shift + T,新建或者跳转到已创建的UT,这里我们点击Create New Test…新建UT,选择JUnit4,选择好需要测试的方法后点击OK。

3.2、Mock测试相关注解

@Mock注解

@RunWith(PowerMockRunner.class)
public class AnnouncementManagerImplTest {@MockAnnouncementMapper announcementMapper;@MockAnnouncementRepository announcementRepository;@InjectMocks@SpyAnnouncementManagerImpl announcementManagerImpl = new AnnouncementManagerImpl();@Beforepublic void before() {List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());}
}

从上面代码可以看出,被@Mock修饰后的变量announcementMapper则会产生一个AnnouncementMapper的mock类,不用@mock注解用mock方法来产生一个announcementMapper是一样的效果。

@InjectMocks注解

@RunWith(PowerMockRunner.class)
public class AnnouncementManagerImplTest {@MockAnnouncementMapper announcementMapper;@MockAnnouncementRepository announcementRepository;@InjectMocks@SpyAnnouncementManagerImpl announcementManagerImpl = new AnnouncementManagerImpl();@Beforepublic void before() {List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());}
}

@InjectMocks注解修饰的类不能修饰抽象或接口类,被它修饰后,其被mock后的成员变量会注入
到@InjectMocks的类中。
比如上面的announcementMapper则会被注入到announcementManagerImpl中,当调用announcementManagerImpl时,原代码里的announcementMapper.selectCountByParams(queryWrapper)方法则会执行mock方法

调用PowerMockito.spy()方法

通过spy,Whilebox.setInternelState方法设置内部依赖

public class AnnouncementManagerImplTest {AnnouncementMapper announcementMapper;AnnouncementRepository announcementRepository;AnnouncementManagerImpl announcementManagerImpl;@Beforepublic void before() {MockitoAnnotations.openMocks(this);announcementManagerImpl = PowerMockito.spy(new AnnouncementManagerImpl());announcementMapper = PowerMockito.mock(AnnouncementMapper.class);Whitebox.setInternalState(announcementManagerImpl,"announcementMapper",announcementMapper);List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());}
}

Mock使用方式或者技巧

在测试类上加注解@RunWith(PowerMockRunner.class)
等同于在@Before修饰的before方法中添加MockitoAnnotations.openMocks(this);

@Spy等同于PowerMockito.spy()方法

@Mock等同于PowerMockito.mock()方法

但是调用PowerMockito的方法时,必须要调用Whitebox.setInternalState()设置内部依赖

静态方法mockStatic

@RunWith(PowerMockRunner.class)
@PrepareForTest(IdUtils.class)
public class AnnouncementManagerImplTest {@MockAnnouncementMapper announcementMapper;@MockAnnouncementRepository announcementRepository;@InjectMocks@SpyAnnouncementManagerImpl announcementManagerImpl = new AnnouncementManagerImpl();@Beforepublic void before() {PowerMockito.mockStatic(IdUtils.class);List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(IdUtils.randomUUID()).thenReturn("sahduiashdi");PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());}
}

当需要mock静态方法的时候,必须要加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是静态方法所在的类。
添加完注解再使用PowerMockito.mockStatic()方法,mock静态类。

@PrepareForTest的使用场景

当使用PowerMockito.whenNew方法时,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是需要mock的new对象代码所在的类。

当需要mock final方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是final方法所在的类。

当需要mock静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是静态方法所在的类。

当需要mock私有方法的时候, 只是需要加注解@PrepareForTest,注解里写的类是私有方法所在的类

当需要mock系统类的静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解里写的类是需要调用系统方法所在的类

阻止代码初始化,包括static{}静态代码块或者static变量的初始化

@SuppressStaticInitializationFor("cn.com.jc.utils.IdUtils")

3.3、问题

1、使用@InjectMocks修饰接口类报错

解决方法:
①收到new一个实现类的对象

@InjectMocksAnnouncementManager announcementManager = new AnnouncementManagerImpl();

②可以在@Before修饰的before()方法中添加spy方法注入该实现类对象

    @InjectMocksAnnouncementManagerImpl announcementManager;@Beforepublic void before() {announcementManager = PowerMockito.spy(new AnnouncementManagerImpl());}

java单元测试之mock篇相关推荐

  1. Java单元测试之Mock框架

    一.引言 二.为什么要用Mock 三.Mock使用场景 四.Mock定义 五.Mock框架 五.Mockito 5.1 Mockito基本使用 5.2 MockMVC测试 5.2.1 初始化MockM ...

  2. java单元测试之Mock测试编写

    mock框架官方文档地址:https://javadoc.io/static/org.mockito/mockito-core/3.2.0/org/mockito/Mockito.html#verif ...

  3. Java单元测试之模拟利器-使用PowerMock进行Mock测试

    首页 国产Linux Linux命令 openSUSE ArchLinux Slackware FreeBSD Ubuntu CentOS Fedora Debian PHP教程 在线教程 登录 注册 ...

  4. Java单元测试之JUnit4详解

    2019独角兽企业重金招聘Python工程师标准>>> Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @B ...

  5. Java基础学习总结(24)——Java单元测试之JUnit4详解

    Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before  ...

  6. 单元测试之mock使用

    目录 一.简介 二.使用mock做单元测试的优点 1.效率高 2.TDD(测试驱动开发) 3.并行开发 4.解决环境依赖问题 三.mock实现原理 四.mock使用 1.导入Maven依赖包 2.编写 ...

  7. SpringBoot单元测试之mock静态方法

    The article summary 1 为什么要对静态方法mock 2 如何使用`powermock`对静态方法`mock` 2.1 添加依赖 2.2 编写测试类 2.3 需要注意的问题 1 为什 ...

  8. 《Pragmatic Unit Testing In Java with JUnit》—单元测试之道读后感

    <Pragmatic Unit Testing In Java with JUnit>                                                    ...

  9. java单元测试总结

    java单元测试(使用junit) http://www.cnblogs.com/feiweiwei/archive/2009/06/16/1024623.html     JUnit是由 Erich ...

最新文章

  1. c语言编号程序,c语言中如何在一个程序语句前加编号c语言中如何在一个程序语句前加...
  2. SQL Server 2005/2008 图形界面(SQL Server Management Studio Express)
  3. [SOJ] connect components in undirected graph
  4. 【LeetCode】0046.全排列 (递归详解)
  5. .Net解析html文档类库HtmlAgilityPack完整使用说明
  6. python3.9教程_Python3简明教程(九)—— 文件处理
  7. 机器学习中数据集的拆分
  8. java dll 乱码_java调用c++ dll出现中文乱码 | 学步园
  9. 关于php的函数,总结关于PHP文件函数有哪些
  10. 第12天--css三种定位及z-index
  11. 【数据预测】基于matlab双向长短时记忆BiLSTM(多输入单输出)数据预测【含Matlab源码 1826期】
  12. ubuntu下安装matlab及配置vot-toolkit
  13. nginx工作原理及配置
  14. 1.3-----Simplify 3D切片软件简单设置
  15. matlab 盲源信号分离,基于盲源分离的图像噪声滤除的研究(附Matlab仿真程序)☆
  16. @Value注入静态变量(static)
  17. androbench跑分性能排查
  18. 批处理判断操作系统的文件系统类型
  19. Unity 图片定点缩放功能
  20. select查询之联合查询

热门文章

  1. 软件工程中过程设计的工具
  2. 行业轮动策略(思想+源码)
  3. jQuery的介绍与应用
  4. iOS录屏/自定义相机
  5. am335x 的MUSB控制器解读
  6. VC++实现简单HTTP服务器
  7. 【论文精读】Shape-Optimizing and Illumination-Smoothing Image Stitching
  8. 什么样的人才是一个成熟的人?
  9. php 点击按钮触发微信分享,微信自定义分享-PHP
  10. Quartus II 安装教程—FPGA入门教程