- 单元测试【就像买保险,希望自己不要用上】是重构的保护网:单元测试可以为重构提供信心,降低重构的成本。
我们要像重视生产代码那样,重视单元测试【元测试(Unit Testing)是针对程序模块(软件设计的最小单位,面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法)进行的正确性检验测试工作。】。持续集成也要依赖单元测试,当持续集成服务自动构建新代码之后,会自动运行单元测试来发现代码错误。
- JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,
基于JIT动态编译技术,会将热点代码转换成机器码,直接交给CPU执行
。所以,顺之,JMH出现了。JMH,全称 Java Microbenchmark Harness (微基准测试框架),是专门用于Java代码微基准测试的一套测试工具API,是由 OpenJDK/Oracle 官方发布的工具
。何谓 Micro Benchmark 呢?简单地说就是在 method 层面上的 benchmark,精度可以精确到微秒级
。- Java的基准测试需要注意的几个点:
- 测试前需要预热。
- 防止无用代码进入测试方法中。
- 并发测试。
- 测试结果呈现。
- JMH的使用场景:定量分析某个热点函数的优化效果、想定量地知道某个函数需要执行多长时间,以及执行时间和输入变量的相关性、对比一个函数的多种实现方式
- JMH的DEMO演示,和常用的注解参数:快速掌握JMH这工具的使用:使用maven构建JMH测试项目+运行 JMH 基准测试有两种方式,一个是生产jar文件运行,另一个是直接写main函数或者放在单元测试中执行。+相关的注解【@BenchmarkMode:微基准测试类型、@Warmup:预热,iterations = 3就是指预热轮数、@Measurement:正式度量计算的轮数、@Threads:每个进程中的测试线程、@Fork:进行 fork 的次数。如果 fork 数是3的话,则 JMH 会 fork 出3个进程来进行测试、@OutputTimeUnit:基准测试结果的时间类型。一般选择秒、毫秒、微秒。、@Benchmark:方法级注解,表示该方法是需要进行 benchmark 的对象,用法和 JUnit 的 @Test 类似。】
- 基于JMH可以对很多工具和框架进行测试,比如日志框架性能对比、BeanCopy性能对比 等,(更多的example可以参考官方给出的JMH samples)
- Java的基准测试需要注意的几个点:
- 需要经常使用postman或者swagger之类的进行代码自测,阿里巴巴开发手册,第8条规则(单元测试的基本目标:语句覆盖率达到 70%;核心模块的语句覆盖率和分支覆盖率都要达到 100%)
- 配置覆盖:基于mockmvc的编写的测试用例,由于加载了Spring的配置,会对项目发起真实的调用。如果,环境的配置为线上配置,容易出现安全问题;一般,处于安全考虑,很多公司会对真实环境的修改操作做事务回滚操作,甚至根本就不会进行真实环境的调用,使用模拟环境替换,例如数据库的操作可以使用h2内存数据库进行替换。
- 配置覆盖:基于mockmvc的编写的测试用例,由于加载了Spring的配置,会对项目发起真实的调用。如果,环境的配置为线上配置,容易出现安全问题;一般,处于安全考虑,很多公司会对真实环境的修改操作做事务回滚操作,甚至根本就不会进行真实环境的调用,使用模拟环境替换,例如数据库的操作可以使用h2内存数据库进行替换。
- 怎样才能算单元测试呢? 对于单元测试的定义主要取决于你的项目,
一个函数甚至是一个类都可以看作是一个单元【在计算机编程中,单元测试(Unit Testing)是针对程序模块(软件设计的最小单位)进行的正确性检验测试工作。程序单元是应用的 最小可测试部件 。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。】
。就比如说我们写了一个计算个人股票收益率的方法,我们为了验证它的正确性专门为它写了一个单元测试。再比如说我们代码有一个类专门负责数据脱敏,我们为了验证脱敏是否符合预期专门为这个类写了一个单元测试。一般api的单元测试用例,编写两类,如下:业务参数的校验,和义务异常的校验。例如,名称是否为空,电话号码是否正确,用户未登陆则抛出未登陆异常
。各类业务场景的真实测试用例,例如,编写成功添加顶级菜单的测试用例,已经编写成功添加子级菜单的测试用例
。
- 由于每个单元有独立的逻辑,在做单元测试时,为了隔离外部依赖,确保这些依赖不影响验证逻辑,我们经常会用到 Fake、Stub 与 Mock 。
Java开发springboot项目都是基于junit测试框架
,比较MockitoJUnitRunner与SpringRunner与使用,MockitoJUnitRunner基于mockito,模拟业务条件,验证代码逻辑。SpringRunner是MockitoJUnitRunner子类,集成了Spring容器,可以在测试的根据配置加载Spring bean对象。在Springboot开发中,结合@SpringBootTest注解,加载项目配置,进行单元测试
。- 基于MockitoJUnitRunner的方法测试:
- 基于SpringRunner的Spring容器测试:
- 基于MockitoJUnitRunner的方法测试:
- JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,
- TDD 测试驱动开发:TDD 即 Test-Driven Development( 测试驱动开发),这是敏捷开发的一项核心实践和技术,也是一种设计方法论。
TDD 原理是开发功能代码之前,先编写测试用例代码,然后针对测试用例编写功能代码,使其能够通过
。- 越重要的代码,越要写单元测试;
- 代码做不到单元测试,多思考如何改进,而不是放弃
边写业务代码,边写单元测试,而不是完成整个新功能后再写
;- 多思考如何改进、简化测试代码
- 测试代码需要随着生产代码的演进而重构或者修改,如果测试不能保持整洁,只会越来越难修改。
- 任务分解是做好 TDD 的关键点。只有把任务分解到可以测试的地步,才能够有针对性地写测试。
巨人的肩膀:
javaGuide
Head First Java
effective Java
B站各位老师
极客时间Java基础讲解