单元测试(Junit)

系统—模块—子模块,子模块中不可分割的程序单元的测试,单元的粒度根据实际情况可能是 类或方法等。
面向对象编程中,最小单元就是方法。

单元测试目的是在集成测试和功能测试之前对系统可测试单元进行逐一检查和验证。

单元测试基本原则

Automatic自动化

单元测试应该是全自动执行,测试用例通常会被频繁地触发执行。
单元测试不允许使用System.out人工验证,而必须使用断言来验证

Independent独立性

用例之间不允许相互调用,也不允许执行次序的先后依赖
如下testMethod2需要调用testMethod2会导致运行效率降低

@Test
public void testMethod1(){}@Test
public void testMethod2(){testMethod1();
}

主流测试框架中,JUnit的用例执行顺序是无序的
TestNG支持测试用例的顺序执行(默认测试类用例按照字典升序执行,也可以通过XML或注解Priority方式来配置执行顺序)

Repeatable可重复性

不受外界影响,比如单元测试通常被放到持续集成中,每次有代码提交,单元测试都会被触发。

为了保证被测试模块的交付质量,需要符合BCDE原则

  • Border边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序
  • Correct正确输入并得到预期结果
  • Design与设计文档相结合
  • Error证明程序有错,如强制输入非法数据、异常流程,非业务允许输入来得到预期错误结果

Mock

弥补一些不具备的因素

  • 功能因素,比如被测试方法内部调用的功能不可用
  • 时间因素,比如双十一还没有到来,与此时间相关的功能点
  • 环境因素,支付宝政策、多端环境PC或手机
  • 数据因素,线下数据样本太小,难以覆盖真实场景
  • 其他因素,负载的依赖采用Mock方式实现

最简单的Mock方式是硬编码
最优雅的是使用配置文件
最佳方式是使用Mock框架,例如JMockit/EasyMock/JMock等

单元测试覆盖率

粗粒度

类覆盖:类中只要有方法或变量被测试用例调用或执行到
方法覆盖:测试用例执行过程中,某个方法被调用了。

细粒度

①、行覆盖:也称语句覆盖,度量可执行语句是否被执行到。
公式:执行到语句的行数/总的可执行语句行数

public class ConverageSampleMethods{public Boolean testMethod(int a,int b,int c){boolean result = false;if(a==1 && b==2 || c==3){result = true;}return result;}
}

以上方法有5个可执行语句和3个入参

@Test
@DisplayName("line converage sample test")
void testLineConverageSample(){CoverageSampleMethods coverageSampleMethods = newCoverageSampleMethods(); Assertions.assertTrue(coverageSampleMethods.testMethod( 1 , 2, 0));
}

以上测试用例的行覆盖率是 100% ,但是在执行过程中 c==3 的条件判断根本没有被执行到, a!=I 并且 c!=3 的情况难道不应该测试一下吗?由此可见,行覆盖的覆盖强度并不高,但由于容易计算,因此在主流的覆盖率工具中,它依然是一个十分常见的参考指标。

②、分支覆盖:也称为判定覆盖,用来度量程序中每 个判定分支是否都被执行到。
公式:代码中被执行到的分支数/所有分支的总数

③、条件判定覆盖:要求设计足够的测试用例,能够让判定中每个条件的所有可能情况至少被执行一次 同时每个判定本身的所有可能结果也至少执行一次。

@ParameterizedTest//定义一个参数化测试
@DisplayName("Condition Decision coverage sample test result true")
@CsvSource({"0,2,3","1,0,3",
})//通过定义一个Stirng数组来定义多次运行时的参数列表,
void testConditionDecisionCoverageTrue(int a,int b,int c){CoverageSampleMethods coverageSampleMethods = newCoverageSampleMethods() ; Assertions assertTrue(coverageSampleMethods.testMethod(a, b, c)) ;
}@DisplayName("Condition Decisior coverage sample test result false")
void testConditionDecisionConverageFalse(){CoverageSampleMethods coverageSampleMethods = newCoverageSampleMethods() ; Assertions assertTrue(coverageSampleMethods.testMethod(0, 0, 0));
}

④、条件组合覆盖:是指判定中所有条件的各种组合情况都出现至少一次。

@ParameterizeTest
@DisplayName("Multiple Condition Converage sample test result true")
@CsvSource({"1,2,3","1,2,0","1,0,3","0,2,3","0,0,3",
})
void testMultipleConditionConverageSampleTrue(int a,int b,int c){CoverageSampleMethods coverageSampleMethods= new CoverageSampleMethods();Assertions.assertTrue(coverageSampleMethods.testMethod(a,b,c)) ;
}@ParameterizeTest
@DisplayName("Multiple Condition Converage sample test result false")
@CsvSource({"1,0,0","0,0,0","0,2,0",
})
void testMultipleConditionConverageSampleTrue(int a,int b,int c){CoverageSampleMethods coverageSampleMethods= new CoverageSampleMethods();Assertions.assertTrue(coverageSampleMethods.testMethod(a,b,c)) ;
}

这组测试用例同时满足了( a= !, b2, c3 )为( true, true, true )、( true,true, false )、( true, false, true )、( true, false, false )、( false, true, true )、( false,true, false )、( false, false, true )、( false, false , false )这 种情况。对于一个包含了 个条件的判定 至少需要 个测试用例才可以。虽然这种覆盖足够严谨,但无疑给编写测试用例增加了指数级的工作量。

⑤、路径覆盖:要求能够测试到程序中所有可能的路径
在 testMethod 方法中,可能的路径有 a1,b2,c==3 b!=2 c! =3 a! ,c !=1 c! =3种。当存在“||”时 如果第一个条件已经为 true不再计算后边表达式的值。而当存在“&& 时,如果第一个条件已经为 false同样不再计算后边表达式的值。满足路径覆盖的测试用例如下

@ParameterizeTest
@DisplayName("Path coverage sample test result true")
@CsvSource({"1,2,0","1,0,3","0,0,3",
})
void testMultipleConditionConverageSampleTrue(int a,int b,int c){CoverageSampleMethods coverageSampleMethods= new CoverageSampleMethods();Assertions.assertTrue(coverageSampleMethods.testMethod(a,b,c)) ;
}

单元测试编写

JUnit 和TestNG 几乎始终处于市场前两位单元测试框架

JUnit常用的注解
在这里插入图片描述
测试用例的方法命名规范:

  • 传统方式test开头,testDecodeUserTokenSuccess
  • should—When,shouldSuccessWhenDecodeUserToken
//定义一个测试类并指定用例在测试报告展示的名称
@DisplayName("售票器类型测试")
public class TicketSellerTest{//定义待测类的实例private TicketSeller ticketSeller;/**定义在整个测试类开始前执行通常包括全局和外部资源(包括测试桩)的创建和初始化*/@BeforeAllpublic static void init(){}/**定义在整个测试类完成后执行的操作通常包括全局和外部资源的释放和销毁*/@AfterAllpublic static void cleanup(){}/**定义在每个测试用例开始前执行的操作通常包括基础数据和运行环境的准备*/@BeforeEachpublic void create(){this.ticketSeller = new TicketSeller();}/**定义在每个测试用例完成后的操作通常包括运行环境的清理*/@AfterEachpublic void destroy(){}/**测试用例,当车票售出后余额测试用例,余额不足应该报错*/@Test@DisplayName("售票后余票应减少")public void shouldReduceInventoryWhenTicketSoldOut(){ticketSeller.setInventory(10);ticketSeller.sell(1);assertThat(ticketSeller.getInventory()).isEqualTo(9);}@Test@DisPlayName("余票不足应报错")public void shoudThrowExceptionWhenNoEnoughInventory(){ticketSeller.setInventory(0);assertThatExceptionOfType(TicketException.class).isThrownBy(()->{dicketSeller.sell(1)}).withMessageContaining("all ticket sold out").withNoCause();}/**Disabled将金庸测试用例该测试用例会出现在最终的报告中,但不会被执行*/@Disabled@Test@DisplayName("有退票时余额应增加")public void shouldIncreateInventoryWhenTicketRefund(){ticketSeller.setInventory(10);ticketSeller.refund(1);assertThat(ticketSeller.getInventory()).isEqualTo(11);}
}

在这里插入图片描述
对于使用Maven或Gradle等命令方式运行单元测试,该注解的内容被忽略,单元测试出错
在这里插入图片描述

当测试用例比较多,为了更好组织测试的结构
推荐使用JUnit的@Nested注解表达层次关系

@DisplayName("交易服务测试")
public class TransactioServiceTest{@Nested@DisplayName("用户交易测试")class UserTransactionTest{@Nested@DisplayName("正向测试用例")class PositiveCase(){}@Nested@DisplayName("负向测试用例")class NegativeCase(){}		}@Nested@DisplayName("商家交易测试")class CompanyTransactionTest{}
}

使用分组测试,在不同场景下选择执行响应的测试用例

  • 执行很快且很重要的冒烟测试用例
  • 执行很慢但同样比较重要的日常测试用例
  • 数量很多但不太重要的回归测试用例
@DisplayName("售票器类型测试")
public class TicketSellerTest{@Test@Tag("fast")@DisplayName("售票后余票应减少")public void shouldReduceInventoryWhenTicketSoldOut(){}@Test@Tag("slow")@DisplayName("一次性购买20张车票")public void shouldSuccessWhenBuy20TicketsOnce(){}
}

通过标签选择执行用例类型
在Maven中可以通过配置maven-surefire-pIugin插件来实现

<build><plugins><plugin><artifactid>maven-surefire-plugin</artifactid><version>2.22.0</version><configuration><properties><includeTags>fast</includeTags><excludeTags>slow</excludeTags></properties></configuration></plugin></plugins>
</build> 

在Gradle中通过JUnit专用的junitPlatform配置来实现

junitPlatForm{filters{engines{include 'junit-jupiter','junit-vintage'}tags{include 'fast'exclude 'slow'}}
}

使用@TestFactory注解将数据的输入和输出与测试逻辑分开

@DisplayName("售票器类型测试")
public class ExchangeRateConverterTest{@TestFactory@DisplayName("时间售票检查")Stream<DynamicTest> oddNumberDynamicTestWithStream(){ticketSeller.setCloseTime(LocalTime.of(12,20,25,0));return Stream.of(Lists.list("提前购票",LocalTime.of(12,20,24,0),true),Lists.list("准点购买",LocalTime.of(12,20,25,0),true),Lists.list("晚点购票",LocalTime.of(12,20,26,0),false);).map(data -> DynamicTest.dynamicTest((String)data.get(0),()->assertThat((ticketSeller.cloudSellAt(data.get(1))).ieEqualTo(data.get(2)));}
}

断言与假设

常用的断言方法
在这里插入图片描述
在这里插入图片描述

静态方法
在这里插入图片描述
除了JUnit断言,还有AssertJ流式断言

/**使用JUnit断言
*/
public class JUnitSampleTest{@Testpublic void testUsingJunitAssertThat(){//字符串判断String s = "abcde";Assertions.assertTrue(s.startsWith("ab"));Assertions.assertTrue(s.endsWith("de"));Assertions.assertEquals(5,s.length());//数字判断Integer i = 50;Assertions.assertTrue(i > 0);Assertions.assertTrue(i < 100);//日期判断Date date1 = new Date();Date date2 = new Date(date1.getTime() + 100);Date date3 = new Date(date1.getTime() - 100);Assertions.assertTrue(date1.before(date2));Assertions.assertTrue(date1.after(date3));//List判断List<String> list = Arrays.asList("a","b","c","d");Assertions.assertEquals("a",list.get(0));Assertions.assertEquals(4,list.size());Assertions.assertEquals("d",list.get(list.size() - 1))//Map判断Map<String,Object> map = new HashMap<>();map.put("A",1);map.put("B",1);map.put("C",1);Set<String> set = map.keySet();Assertions.assertEquals(3,map.size());Assertions.assertTrue(set.containsAll(Arrays.asList("A","B","C")));}
}
/**使用AssertJ的断言
*/
public class AssertJSampleTest{@Testpublic void testUsingAssertJ(){//字符串判断String s = "abcde";Assertions.assertThat().as("字符串判断:判断首尾及长度").startsWith("ab").endsWith("de").hasSize(5);//数字判断Integer i = 50;Assertions.assertThat(i).as("数字判断").isGreaterThan(0).isLessThan(100);//日期判断Date date1 = new Date();Date date2 = new Date(date1.getTime() + 100);Date date3 = new Date(date1.getTime() - 100);Assertions.assertThat(date1).as("日期判断:日期大小比较").isBefore(date2).isAfter(date3);}//List判断List<String> list = Arrays.asList("a","b","c","d");Assertions.assertThat(list).as("List的判断:首尾元素及长度").startsWith("a").endsWith("d").hasSize(4);//Map判断Map<String,Object> map = new HashMap<>();map.put("A",1);map.put("B",1);map.put("C",1);Assertions.assertThat(map).as("Map的判断:长度及key值").hasSize(3).containsKeys("A","B","C");
}

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

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

相关文章

这个自动化框架吧,解决接口间数据依赖

在实际的测试工作中&#xff0c;在做接口自动化测试时往往会遇到接口间数据依赖问题&#xff0c;即API_03的请求参数来源于API_02的响应数据&#xff0c;API_02的请求参数又来源于API_01的响应数据。 因此通过自动化方式测试API_03接口时&#xff0c;需要预先请求API_02接口&a…

JeecgBoot入门

最近在了解低代码平台&#xff0c;其中关注到gitee上开源项目JeecgBoot&#xff0c;JeecgBoot官方也有比较完整的入门教学文档&#xff0c;这里我们将耕者官方教程学习&#xff0c;并将其记录下来。 一、项目简介 JeecgBoot 是一款基于代码生成器的低代码开发平台拥有零代码能力…

修改HarmonyOS鸿蒙图标和名字,打包后安装到真机,应用图标丢失变成透明,修改名字也不生效,还是默认的labeL解决方案教程

HarmonyOS鸿蒙打包hap 安装应用到桌面没有图标&#xff0c;用hdc安装到真机&#xff0c;打包后应用图标丢失变成透明&#xff0c;名字也还是默认的label的bug&#xff0c;以下是解决方案 以下是修改方案&#xff1a; 1、修改应用名字&#xff1a; 2、修改应用图标&#xff1a…

MYSQL安装(ubuntu系统)

rpm -qa 查询安装软件包 ps axj 查询服务 卸载mysql&#xff08;万不得已&#xff09; ps axj | grep mysql 查看是否存在mysql服务 systemctl stop mysqld 关闭该服务 rpm -qa | grep mysql 查安装mysql安装包 rmp -qa | grep mysql | xargs (yum apt) -y remove进行批量…

比ChatGPT更牛!苹果新AI模型刷新交互体验!能看懂你的手机屏幕!平板和安卓机也都行

家人们&#xff0c;苹果一直在悄悄进步&#xff01; 近期&#xff0c;据小鹿观察&#xff0c;各大科技巨头不仅在提升模型解决复杂问题的能力上竞争激烈&#xff0c;而且还在大语言模型应用于用户界面&#xff08;UI&#xff09;交互方面上暗暗发力&#xff01; 最近&#xf…

InstructIR: High-Quality Image Restoration Following Human Instructions 论文阅读笔记

这是Radu大佬所在的Wrzburg大学的computer vision lab实验室发表在ECCV2024上的一篇论文&#xff0c;代码开源。文章提出了一种文本引导的All-in-One的restoration模型&#xff0c;如下图所示&#xff1a; 这个工作其实跟"InstructPix2Pix: Learning to Follow Image Edit…

解决使用Golang的email库发送qq邮件报错short response,错误类型为textproto.ProtocolError

问题阐述 使用email库发送QQ邮件&#xff0c;采用587端口&#xff1a; package mainimport ("fmt""net/smtp""github.com/jordan-wright/email" )func SendEmail(sendTo string, subject string, body string) (err error) {e : email.NewEmai…

【北京迅为】《STM32MP157开发板嵌入式开发指南》-第七十二章 Debian文件系统

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

Spring框架的JDBC模板技术

目录 一、JDBC模板类的使用 1.引入依赖 2.测试类 3.运行&#xff0c;查看数据库 二、使用Spring框架来管理模板类 1.配置文件 2.测试类 3.运行&#xff0c;查看数据库 三、Spring框架管理开源的连接池 1.配置开源的连接池 2.将数据库连接的信息配置到属性文件中 3.核…

【万字详文介绍】:迭代扩张卷积神经网络(IDCNN)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

2-Ubuntu/Windows系统启动盘制作

学习目标&#xff1a; 掌握使用Win32DiskImager、Rufus等工具制作系统启动盘的基本步骤。独立将ISO镜像文件写入USB闪存驱动器&#xff0c;确保在需要时顺利安装或修复系统。通过学习如何选择正确的源文件和目标驱动器&#xff0c;理解启动盘的使用场景和注意事项&#xff0c;…

简介Voronoi图Voronoi Diagrams

这是计算机的经典算法。 问题引入 倘若一张大白纸上有很多三角点&#xff0c;掉进去一个五星点&#xff0c;问&#xff0c;哪个三角离着五星最近&#xff1f;简单&#xff0c;算距离呗&#xff0c;这个五星到其他所有三角点的距离&#xff0c;找到最小的那个就行。 若掉进去…

【进阶sql】复杂sql收集及解析【mysql】

开发时会出现&#xff0c;必须写一些较复杂sql的场景 可能是给会sql的客户 提供一些统计sql 或是临时需要统计数据信息但是 开发一个统计功能有来不及的情况 也可能是报表系统组件 只支持 sql统计的情况 特地记录下这些sql 作为积累 substring 截取查询出的字符串&#xff…

SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“

SLF4J常见问题 导入依赖&#xff1a; <dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> </dependency> <dependency><groupId>org.slf4j</groupId><arti…

002-Kotlin界面开发之Kotlin旋风之旅

Kotlin旋风之旅 Compose Desktop中哪些Kotlin知识是必须的&#xff1f; 在学习Compose Desktop中&#xff0c;以下Kotlin知识是必须的&#xff1a; 基础语法&#xff1a;包括变量声明、数据类型、条件语句、循环等。面向对象编程&#xff1a;类与对象、继承、接口、抽象类等。…

RSTP的工作过程

RSTP简介&#xff1a; 生成树协议&#xff08;STP&#xff09;用于在网络中防止环路产生&#xff0c;但 STP 的收敛速度较慢。 RSTP&#xff08;Rapid Spanning Tree Protocol &#xff09;快速生成树协议&#xff1a;RSTP 是对 STP 的改进&#xff0c;它能在网络拓扑发生变化…

【IEEE出版 | EI稳定检索】2024智能机器人与自动控制国际学术会议 (IRAC 2024,11月29-12月1日)

2024智能机器人与自动控制国际学术会议 &#xff08;IRAC 2024&#xff09; 2024 International Conference on Intelligent Robotics and Automatic Control 官方信息 会议官网&#xff1a;www.icirac.org 2024 International Conference on Intelligent Robotics and Autom…

nginx 搭建网站

1.查看防火墙状态systemctl status firewalld 2.getenforce 3.安装nginx yum install nginx -y 4.网站信息 echo "welcome to yinchuankejixuanyuan" > /usr/share/nginx/html/index.html 5.查看命令状态 nginx -t 6.重启 systemctl restart nginx

从0开始搭建一个生产级SpringBoot2.0.X项目(八)SpringBoot 使用Redis

前言 最近有个想法想整理一个内容比较完整springboot项目初始化Demo。 SpringBoot使用Redis 缓存数据 一、 pom引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>&…

Logstash 安装与部署(无坑版)

下载 版本对照关系&#xff1a;ElasticSearch 7.9.2 和 Logstash 7.9.2 &#xff1b; 官方下载地址 选择ElasticSearch版本一致的Logstash版本 https://www.elastic.co/cn/downloads/logstash 下载链接&#xff1a;https://artifacts.elastic.co/downloads/logstash/logst…