单测的思路

文章目录

  • 单测的分类
  • 方法的单测
    • 生成工具的对比
    • 生成步骤
  • 接口的单测
    • mock步骤
    • 部分依赖mock的方式
  • 场景的单测
  • 参考

单测的分类

  • 单元测试(Unit Testing)是一种软件开发中的测试方法,它的主要目的是确保软件中的最小可测试单元(通常是函数、方法或类)在被单独测试和验证时能够按照预期工作。尽管单元测试有很多优点,如提高代码质量、减少Bug、简化调试过程等,但它也存在一些缺点:
    • 增加开发时间:如要求覆盖率到80%甚至90%,或者入参几十个难以构造,单测时间占比可能超过30%。
    • 需要维护:随着代码的改变,特别是大规模的重构,单元测试也需要相应地更新和维护,增加开发的负担。
    • 无法发现对其他类的影响:单元测试主要关注单个单元的行为,无法发现与多个单元交互或整个系统相关的问题。
  • 按照接口、场景维度调用对应的方法。
    • 优点有:
      • 减少开发时间:mock框架搭起来后只需要构造入参、和个别新增的下游依赖。
      • 维护成本低:接口内部的重构不会影响到单测的入参和出参。
      • 可以发现对其他类的影响:一次调用涉及到多个类,可以测出对其他类甚至其他接口的影响。
    • 缺点有:
      • 分支逻辑覆盖困难。如果接口涉及10个串联的类,每个类有2个分支,则需要构造1024个入参,远超方法维度单测的20个。
  • 所以最好能同时使用两种单测,做到优势互补。
    • 方法的单测:覆盖入参少、业务分支多的场景。
    • 接口、场景的单测:覆盖主干接口。

方法的单测

推荐用更智能的squaretest生成单测模板后,手工调整。

生成工具的对比

  • squaretest
    • 优点:
      • 生成测试用例相对智能,自动生成入参、覆盖部分if分支。
    • 缺点:
      • 只有30天的免费试用期,之后需要付费使用。事实上点掉reminder后可以继续使用。
  • TestMe
    • 优点:
      • 简单易用,适合初学者或小型项目使用。
    • 缺点:
      • 生成的测试用例不够全面或深入, 需要手动填充输入参数、写分支逻辑。
  • EvoSuite
    • 优点:
      • 作为Maven插件使用,方便集成到Java项目中。
    • 缺点:
      • 社区支持相对较少,遇到问题时可能难以得到及时帮助。
      • 配置和使用可能相对复杂,需要一定的学习成本,如需要引入groovy等外部依赖
  • diffblue
    • 优点:
      • 与IntelliJ IDEA集成良好,使用方便。
      • 支持多种编程语言和框架。
    • 缺点:
      • 商用版本收费较高,对于个人用户或小型团队可能不太友好。
      • 生成单测较慢,一个类近1Min。

生成步骤

  1. 安装插件
    在这里插入图片描述

  2. 引入依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.1.1.RELEASE</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.2</version></dependency>
  1. 编写业务代码
@Service
public class TestServiceImpl implements TestService {@Resourceprivate TestRepository testRepository;@Resourceprivate TestThird testThird;@Overridepublic void start(InputDTO inputDTO) {if (testRepository.select(inputDTO.getId())==null) {testRepository.insert(new InputEntity());}testThird.callThird(new InputEntity());}
}
  1. 生成单测
    在这里插入图片描述在这里插入图片描述
  2. 单测生成结果
class TestServiceImplTestSquaretest {@Mockprivate TestRepository mockTestRepository;@Mockprivate TestThird mockTestThird;@InjectMocksprivate TestServiceImpl testServiceImplUnderTest;@BeforeEachvoid setUp() {initMocks(this);}@Testvoid testStart() {// Setupfinal InputDTO inputDTO = new InputDTO();inputDTO.setName("name");inputDTO.setId(0);final InputDetail inputDetail = new InputDetail();inputDetail.setName("name");inputDTO.setInputDetail(inputDetail);// Configure TestRepository.select(...).final InputEntity inputEntity = new InputEntity();inputEntity.setId(0);inputEntity.setName("name");when(mockTestRepository.select(0)).thenReturn(inputEntity);// Run the testtestServiceImplUnderTest.start(inputDTO);// Verify the resultsverify(mockTestThird).callThird(any(InputEntity.class));}@Testvoid testStart_TestRepositorySelectReturnsNull() {// Setupfinal InputDTO inputDTO = new InputDTO();inputDTO.setName("name");inputDTO.setId(0);final InputDetail inputDetail = new InputDetail();inputDetail.setName("name");inputDTO.setInputDetail(inputDetail);when(mockTestRepository.select(0)).thenReturn(null);// Run the testtestServiceImplUnderTest.start(inputDTO);// Verify the resultsverify(mockTestRepository).insert(any(InputEntity.class));verify(mockTestThird).callThird(any(InputEntity.class));}
}
class TestServiceImplTestTestMe {@MockTestRepository testRepository;@MockTestThird testThird;@InjectMocksTestServiceImpl testServiceImpl;@BeforeEachvoid setUp() {MockitoAnnotations.initMocks(this);}@Testvoid testStart() {when(testRepository.select(anyInt())).thenReturn(new InputEntity());when(testRepository.insert(any())).thenReturn(Integer.valueOf(0));testServiceImpl.start(new InputDTO());}
}

接口的单测

mock步骤

mock外部依赖,启动容器,调用接口

  1. 编写外部依赖的mock类
@Service
public class TestThirdImpl implements TestThird {@Overridepublic void callThird(InputEntity entity) {System.out.println("TestThirdImpl callThird");}
}
//mock
public class TestThirdMockImpl implements TestThird {public void callThird(InputEntity entity) {System.out.println("TestThirdMockImpl callThird");}
}
  1. 替换容器中的beanDefinition
@Configuration
public class MockConfig {@Beanpublic BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {return new BeanDefinitionRegistryPostProcessor() {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {//移除依赖的beanregistry.removeBeanDefinition("testThirdImpl");//获取Mockbean的定义BeanDefinition beanDe = BeanDefinitionBuilder.rootBeanDefinition(TestThirdMockImpl.class).getBeanDefinition();//注册mockbeanregistry.registerBeanDefinition("testThirdImpl", beanDe);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}};}
}
  1. test模块中启动容器,并调用入口方法
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class TestApplicationTest {@Resourceprivate TestService testService;@Testpublic void start() {testService.start(new InputDTO());}}

部分依赖mock的方式

  1. 数据库。用h2数据库mock。
public class MockDataSource extends HikariDataSource {public MockDataSource() {this.setDriverClassName("org.h2.Driver");this.setJdbcUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL;");this.setRegisterMbeans(true);this.setPoolName("mock");}
}

场景的单测

将接口单测组合

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class TestApplicationTest {@Resourceprivate TestService testService;@Testpublic void start() {testService.start(new InputDTO());testService.end(new InputDTO());}}

参考

  • 告别加班/解放双手提高单测覆盖率之Java 自动生成单测代码神器推荐
  • 单元测试 - 单元测试集成方案
  • JUnit 5 User Guide
  • 关于testNG和JUnit的对比
  • JUnit 5 单元测试教程
  • Junit5文档
  • 单元测试自动生成工具EvoSuite的简单使用
  • 使用BeanDefinitionRegistryPostProcessor动态注入BeanDefinition
  • 使用h2数据库支持单元测试
  • 使用H2数据库来模拟进行单元测试
  • H2官网
  • 关于springboot项目(@SpringBootTest单元测试类)找不到配置文件问题
  • Run Both JUnit 4 and Junit5 With Maven Surefire Plugin
  • 解决 Spring Boot 启动时 TypeNotPresentExceptionProxy / ArrayStoreException 异常
  • Maven的单元测试没有执行的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/259135.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(07)Hive——窗口函数详解

一、 窗口函数知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowing…

英文论文(sci)解读复现【NO.21】一种基于空间坐标的轻量级目标检测器无人机航空图像的自注意

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

红队笔记Day3-->隧道上线不出网机器

昨天讲了通过代理的形式&#xff08;端口转发&#xff09;实现了上线不出网的机器&#xff0c;那么今天就来讲一下如何通过隧道上线不出网机器 目录 1.网络拓扑 2.开始做隧道&#xff1f;No&#xff01;&#xff01;&#xff01; 3.icmp隧道 4.HTTP隧道 5.SSH隧道 1.什么…

探索未来科技前沿:深度学习的进展与应用

深度学习的进展 摘要&#xff1a;深度学习作为人工智能领域的重要分支&#xff0c;近年来取得了巨大的进展&#xff0c;并在各个领域展现出惊人的应用潜力。本文将介绍深度学习的发展历程、技术原理以及在图像识别、自然语言处理等领域的应用&#xff0c;展望深度学习在未来的…

RunnerGo:UI自动化测试神器!

UI自动化测试已经成为现代软件开发过程中不可或缺的一部分。它能够提供诸多优势&#xff0c;包括提高测试效率、减少人力成本、提升软件质量等。同时&#xff0c;可视化工具为UI自动化测试带来了更多便利和灵活性。RunnerGo近期上线脚本录制器&#xff0c;根据你的测试操作直接…

React 更改程序入口点(index.js文件位置变更)

食用前提示&#xff1a;本文基于已经快速配置好的React环境而作&#xff0c;配置React环境详见拙作&#xff1a;React环境配置-CSDN博客~ 一、了解默认入口点 使用create-react-app快速搭建react环境后&#xff0c;npm start启动程序的默认入口点为/src/index(即src目录下的ind…

【Prometheus】组件介绍-工作流程-部署模式-数据类型-监控

基于Prometheus和K8S构建智能化告警系统 一、Prometheus简介二、Prometheus特点于样本2.1、特点2.2、样本 三、Prometheus组件介绍四、Prometheus工作流程五、Prometheus的几种部署模式5.1、基本高可用模式5.2、基本高可用远程存储5.3、基本高可用远程存储联邦集群 六、Prometh…

ESP32工程中CMake使用及加入第三方SDK库文件

1、ESP32工程结构 本文中使用的是乐鑫官方推出的ESP-IDF v5.1对ESP32S3设备开发&#xff0c;并非是Arduino、Micro-python等第三方工具开发。在ESP-IDF框架中&#xff0c;乐鑫官方已经将CMake 和 Ninja 编译构建工具集成到了ESP-IDF中。 ESP-IDF 即乐鑫物联网开发框架&#xff…

卷积神经网络的基本结构

卷积神经网络的基本结构 与传统的全连接神经网络一样&#xff0c;卷积神经网络依然是一个层级网络&#xff0c;只不过层的功能和形式发生了变化。 典型的CNN结构包括&#xff1a; 数据输入层&#xff08;Input Layer&#xff09;卷积层&#xff08;Convolutional Layer&#x…

第11章 GUI

11.1 Swing概述 Swing是Java语言开发图形化界面的一个工具包。它以抽象窗口工具包&#xff08;AWT&#xff09;为基础&#xff0c;使跨平台应用程序可以使用可插拔的外观风格。Swing拥有丰富的库和组件&#xff0c;使用非常灵活&#xff0c;开发人员只用很少的代码就可以创建出…

力扣OJ题——旋转数组

题目&#xff1a;189.旋转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数 思路一&#xff1a; 1.每次挪动旋转1位&#xff08;用tmp将最后一位存起来&#xff0c;其余所有数据向后移&#xff0c;然后将tmp放在第一个位…

Python编程中的异常处理

什么是异常&#xff1f; 程序错误&#xff08;errors&#xff09;有时也被称为程序异常&#xff08;exceptions&#xff09;&#xff0c;这是每个编程人员都会经常遇到的问题。在过去&#xff0c;当遇到这类情况时&#xff0c;程序会终止执行并显示错误信息&#xff0c;通常是…

Qt:Qt3个窗口类的区别、VS与QT项目转换

一、Qt3个窗口类的区别 QMainWindow&#xff1a;包含菜单栏、工具栏、状态栏 QWidget&#xff1a;普通的一个窗口&#xff0c;什么也不包括 QDialog&#xff1a;对话框&#xff0c;常用来做登录窗口、弹出窗口&#xff08;例如设置页面&#xff09; QDialog实现简易登录界面…

力扣经典题:环形链表的检测与返回

1.值得背的题 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode *detectCycle(struct ListNode *head) {struct ListNode*fasthead;struct ListNode*slowhead;while(fast!NULL&&fast->…

LCR 127. 跳跃训练【简单】

LCR 127. 跳跃训练 题目描述&#xff1a; 今天的有氧运动训练内容是在一个长条形的平台上跳跃。平台有 num 个小格子&#xff0c;每次可以选择跳 一个格子 或者 两个格子。请返回在训练过程中&#xff0c;学员们共有多少种不同的跳跃方式。 结果可能过大&#xff0c;因此结果…

spring boot自动装配

第一步需要在pom.xml文件指定需要导入的坐标 要是没有自动提示需要检查maven有没有 实现代码 /*springboot第三方自动配置实现方法 * 什么是自动配置 自动配置就是springboot启动自动加载的类不需要在手动的控制反转自动的加入bean中 * * *//*第一种方案包扫描 不推荐因为繁琐…

The Captainz NFT 概览与数据分析

作者&#xff1a;stellafootprint.network 编译&#xff1a;cicifootprint.network 数据源&#xff1a;The Captainz NFT Collection Dashboard The Captainz 是 Memeland 的旗舰系列&#xff0c;由 9,999 个实用性极强的 PFP 组成。持有者在 Memeland 宇宙中展开了一场神…

[word] word参考文献怎么对齐 #学习方法#微信#笔记

word参考文献怎么对齐 word参考文献怎么对齐&#xff1f; 未对齐的参考文献如下 全部选中参考文献内容 选中段落快捷窗口显示/隐藏编辑标记快捷方式和标号快捷方式中左对齐 选中之后参考文献又自动加了标号 把之前的角标和文字之间全部删除 完成图

【Java多线程案例】实现阻塞队列

1. 阻塞队列简介 1.1 阻塞队列概念 阻塞队列&#xff1a;是一种特殊的队列&#xff0c;具有队列"先进先出"的特性&#xff0c;同时相较于普通队列&#xff0c;阻塞队列是线程安全的&#xff0c;并且带有阻塞功能&#xff0c;表现形式如下&#xff1a; 当队列满时&…

MySQL数据库⑪_C/C++连接MySQL_发送请求

目录 1. 下载库文件 2. 使用库 3. 链接MySQL函数 4. C/C链接示例 5. 发送SQL请求 6. 获取查询结果 本篇完。 1. 下载库文件 要使用C/C连接MySQL&#xff0c;需要使用MySQL官网提供的库。 进入MySQL官网选择适合自己平台的mysql connect库&#xff0c;然后点击下载就行…