软件测试技术之单元测试—工程师 Style 的测试方法(2)
怎么写单元测试?
JUnit 简介
基本上每种语言和框架都有不错的单元测试框架和工具,例如 Java 的 JUnit、Scala 的 ScalaTest、Python的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的,我们下面就简单介绍下 JUnit。
JUnit 里面每个 @Test 注解的方法,就是一个测试。@Ignore 可以忽略一个测试。@Before、@BeforeClass、@After、@AfterClass 可以在测试执行前后插入一些通用的操作,比如初始化和资源释放等等。
除了 assertEquals,JUnit 也支持不少其他的 assert 方法。例如 assertNull、assertArrayEquals、assertThrows、assertTimeout 等。另外也可以用第三方的 assert 库比如 Spring 的 Assert 或者 AssertJ。
除了可以测试普通的代码逻辑,JUnit 也可以进行异常测试和时间测试。异常测试是测试某段代码必须抛指定的异常,时间测试则是测试代码执行时间在一定范围内。
也可以对测试进行分组。例如可以分成 contractTest 、mockTest 和 unitTest,通过参数指定执行某个分组的测试。
这里就不做过多介绍了,想了解更多 JUnit 的可以去看 极客学院的 JUnit 教程 等资料。其他的单元测试框架,基本功能都是大同小异。
使用测试 Double
狭义的单元测试,我们是只测试单元本身。即使我们写的是广义的单元测试,它依然可能依赖其他模块,比如其他类的方法、第三方服务调用或者数据库查询等等,造成我们无法很方便的测试被测系统或模块。这时我们就需要使用测试 Double 了。
如果细究的话,测试 Double 分成好多种,比如什么 Dummies、Fakes 等等。但我认为我们只要弄清两类就可以了,也就是 Stub 和 Mock。
Stub
Stub 指那些包含了预定义好的数据并且在测试时返回给调用者的对象。Stub 常被用于我们不希望返回真实数据或者造成其他副作用的场景。
我们契约测试生成的、可以通过 spring cloud stubrunner 运行的 Stub Jar 就是一个 Stub。我们可以让 Stub 返回预设好的假数据,然后在单元测试里就可以依赖这些数据,对代码进行测试。例如,我们可以让用户查询 Stub 根据参数里的用户 ID 返回认证用户和未认证用户,然后我们就可以测试调用方在这两种情况下的处理逻辑了。
当然,Stub 也可以不是远程服务,而是另外一个类。所以我们经常说要针对接口编程,因为这样我们就可以很容易的创建一个接口的 Stub 实现,从而替换具体的类。
public class StubNameService implement NameService {
public String get(String userId) {
return ““Mock user name””;
}
}
public class UserServiceTest {
// UserService 依赖 NameService,会调用其 get 方法
@Inject
private UserService userService;
@Test
public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
userService.setNameService(new StubNameService());
String testName = userService.getUserName(““SomeId””);
Assert.assertEquals(““Mock user name””, testName);
}
}
不过这样要实现很多 Stub 也是很麻烦的,现在我们已经不需要自己创建 Stub 了,因为有了各种 Mock 工具。
Mock
Mocks 指那些可以记录它们的调用信息的对象,在测试断言中我们可以验证 Mocks 被进行了符合期望的调用。
Mock 和 Stub 的区别在于,Stub 只是提供一些数据,它并不进行验证,或者只是基于状态做一些验证;而 Mock 除了可以做 Stub 的事情,也可以基于调用行为进行验证。比如说,Mock 可以验证 Mock 接口被调用了不多不少正好两次,并且调用的参数是期望的数值。
Java 里最常用的 Mock 工具就是 Mockito 了。我们来看一个简单的例子,下面的 UserService 依赖 NameService。当我们测试 UserService 的时候,我们希望隔离 NameService,那么就可以创建一个 Mock 的 NameService 注入到 UserService 中(在 Spring 里只需要用 @Mock 和 @InjectMocks 两个注解就可以完成了)
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Mock
private NameService nameService;
@Test
public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
Mockito.when(nameService.getUserName(““SomeId””)).thenReturn(““Mock user name””);
String testName = userService.getUserName(““SomeId””);
Assert.assertEquals(““Mock user name””, testName);
Mockito.verify(nameService).getUserName(““SomeId””);
}
}
注意上面最后一行,是验证 nameService 的 getUserName 被调用,并且参数为 ““SomeId””。更多关于 Mockito 的内容,可以参考 Mockito 的文档(
http://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/Mockito.html)。
契约测试
契约测试会给每个服务生成一个 Stub,可以用于调用方的单元/集成测试。例如,我们需要测试预约服务的预约操作,而预约操作会调用用户服务,去验证用户的一些基本信息,比如医生是否认证等。
所以,我们可以通过传入不同的用户 ID,让契约 Stub 返回不同状态的用户数据,从而验证不同的处理流程。例如,正常的预约流程的测试用例可能是这样的。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest@AutoConfigureStubRunner(repositoryRoot=““http://<nexus_root>””,
ids = {"“com.xingren.service:user-client-stubs:1.0.0:stubs:6565"”})public class BookingTest {
// BookingService 会调用用户服务,获取医生认证状态后进行不同的处理
@Inject
private BookingService bookingService;
@Test
public void testBooking() {
BookingForm form = new BookingForm(
1,// doctorId
1// scheduleId
1001);// patientId
BookVO res = bookingService.book(form);
assertTrue(res.id > 0);
assertTrue(res.payStatus == PayStatus.UN_PAY);
}
}
注意上面的 AutoConfigureStubRunner 注解就是设置并启动了用户服务 Stub,当然在测试的时候,我们需要把服务调用接口的 baseUrl 设置为http://localhost:6565。关于契约测试的更多内容,请参考微服务环境下的集成测试探索一文。
TDD
简单说下 Test Driven Development,也就是 TDD。左耳朵耗子就写了一篇TDD并不是看上去的那么美,我就直接引用其介绍了。
其开发过程是从功能需求的test case开始,先添加一个test case,然后运行所有的test case看看有没有问题,再实现test case所要测试的功能,然后再运行test case,查看是否有case失败,然后重构代码,再重复以上步骤。
其实严格的 TDD 流程实用性并不高,左耳朵耗子本身也是持批判态度。但是对于接口定义比较明确的模块,先写单元测试再写实现代码还是有很大好处的。因为目标清晰,而且可以立刻得到反馈。
文章来源:网络 版权归原作者所有
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理
相关文章:
软件测试技术之单元测试—工程师 Style 的测试方法(2)
怎么写单元测试? JUnit 简介 基本上每种语言和框架都有不错的单元测试框架和工具,例如 Java 的 JUnit、Scala 的 ScalaTest、Python的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的,我们下面就简单介绍下 JUnit。 JUnit…...
项目中超图 for openlayer和超图for cesium同时引入的问题
一个项目中同时用到了超图的openlayer和cesium版本,首先我是外部引入的超图的开发包,你要是通过npm导入的那就没关系了。 <script type"text/javascript" src"/static/openlayer/supermap/ol/iclient-ol.min.js"></script&…...
3D与沉浸式技术,如何助力企业数字化转型?
说起3D,估计许多读者朋友会在第一时间想起《阿凡达》系列和《侏罗纪公园》系列电影大作。每一帧细节纤毫毕现的逼真画面,让观众几乎分不清虚拟与现实,完全沉浸在导演打造的视觉盛宴中。 事实上,除了大家所熟知的3D影视动画之外&am…...
excel vba 将多张数据表的内容合并到一张数据表
功能描述: 一个Excel文件有很多个 样式相同 的数据表, 需要将多张数据表的内容合并到一张数据表里。 vba实现代码如下: Attribute VB_Name "NewMacros" Option Explicit Public Const Const_OutSheetName As String "V…...
接口和抽象类的区别?解析接口和抽象类的特点和用法
接口和抽象类的区别?解析接口和抽象类的特点和用法 引言 在面向对象编程中,接口和抽象类是两个非常重要的概念。它们都可以用于定义一组相关的方法,但在实际使用中有一些差异。本文将探讨接口和抽象类的区别,并通过示例代码和测…...
vscode-vue项目格式化
一、插件要求 Prettier Vetur 二、配置文件 {"workbench.startupEditor": "newUntitledFile","files.autoSave": "off", // 关闭文件自动保存,避免开发时候页面变化"editor.tabSize": 2, // tab距离"ve…...
SAP MM学习笔记26- SAP中 振替转记(转移过账)和 在库转送(库存转储)1- 移动Type间振替转记
SAP 中在库移动 不仅有入库(GR),出库(GI),也可以是单纯内部的转记或转送。 1,振替转记(转移过账) 2,在库转送(库存转储) 1ÿ…...
SAP SPL(Special Ledger)之注释行项目-Noted Items
财务凭证过账里常见的SPL特殊总账标识根据业务主要有三种,BoE-billing of exchange: 汇票业务,包括商业汇票和银行汇票;Down Payment,预付款业务,包括供应商和客户预付款和申请;其它,一般是保证…...
学习平台助力职场发展与提升
近年来,随着互联网技术的发展,学习平台逐渐成为了职场发展和提升的必备工具。学习平台通过提供丰富的课程内容、灵活的学习时间和个性化的学习路径,帮助职场人士更好地提升自己的技能和知识储备,为职场发展打下坚实的基础。 学习…...
有没有免费格式转换工具推荐?PDF转化为PPT的方法
在当今职场生活中,掌握文件格式转换技能变得异常重要。将PDF文档转换为PPT格式可以在演讲、报告等场合更好地展示和传达信息,为我们的专业形象增添亮点,接下来我们可以一起来看一下“有没有免费格式转换工具推荐?PDF转化为PPT的方法”相关的…...
【LeetCode-经典面试150题-day12】
20.有效的括号 题意: 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括…...
TCP机制-延迟应答,捎带应答
在看本篇博客前推荐先看TCP中窗口和滑动窗口的含义以及流量控制 延迟应答和捎带应答都是TCP用于提高网络传输效率的机制 延迟应答 当发送端发送数据给接收端了以后,按道理接收端的内核会立即返回ACK(应答报文)给发送端,而且ACK&a…...
【Redis从头学-8】Redis中的ZSet数据类型实战场景之用户积分榜
🧑💻作者名称:DaenCode 🎤作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。 😎人生感悟:尝尽人生百味,方知世间冷暖。 📖所属专栏:Re…...
Springboot内嵌SQLite配置使用
版本号 MacOS Apple M1 | Jdk17 | Maven 3.8.5 | SpringBoot 2.6.9 | SQLite 3.42.0.0 pom.xml <dependencies><dependency><groupId>org.xerial</groupId><artifactId>sqlite-jdbc</artifactId><version>3.42.0.0</version&g…...
【微服务学习笔记】认识微服务
【微服务学习笔记】认识微服务 单体架构 分布式架构 微服务架构 SpringCloud 服务拆分和注意事项 服务拆分的案例demo 各个服务之间的数据库都是相互独立的,你不能直接访问对方的数据库,只能从一个服务像另外一个服务发起远程调用 在订单模块的服务中 …...
基于Android R快速编译recovery-ramdisk.img
Android默认没有单编recovery-ramdisk.img的命令,我们可以自己修改Makefile实现 修改:build/core/Makefile 添加: .PHONY: recovery-ramdisk-nodeps recovery-ramdisk-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)echo "make …...
Redis分布式缓存
分布式缓存 -- 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题: 1.Redis持久化 Redis有两种持久化方案: RDB持久化 AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file(Redis数据备份文件)&#x…...
最大公约数和最小公倍数
最大公约数: 概念: 公约数中最大的称为最大公约数。 对任意的若干个正整数,1总是它们的公因数。 公约数与公倍数相反,就是既是A的约数同时也是B的约数的数,12和15的公约数有1,3,最大公约数就是…...
数据结构——二叉搜索树(附带C++实现版本)
文章目录 二叉搜索树概念 二叉树的实际应用二叉树模拟实现存储结构二叉搜索树构成二叉搜索树的查找插入操作中序遍历二叉树的删除循环(利用左子树最右节点)递归(利用右子树根节点) 二叉树拷贝二叉树资源的销毁 二叉树实现完整代码总结 二叉搜索树 概念 二叉搜索树…...
C++(3)C++对C的扩展Extension
类型增强 1、类型更加严格 不初始化,无法通过编译;C不初始化,则随机赋值 #include <iostream> #include <stdlib.h>int main() {const int a 100; //真正的const,无法修改 // int *p &a; 报错const int *p…...
在vscode(idea)使用GitHub账号、Copilot异常
在idea使用GitHub账号、Copilot异常 登录GitHub显示 Invalid authentication data.Connection refused: connect或者副驾驶显示 Failed to initiate the GitHub login process. Please try again.一般网上的方法推荐使用token登录,或者降级副驾驶 经过研究&#x…...
新的后端渲染:服务器驱动UI
通过API发送UI是一种彻底的新方法,将改变传统的UI开发。 一项正在改变我们对用户界面 (UI) 的看法的技术是通过 API 发送 UI,也称为服务器驱动UI。这种方法提供了新水平的活力和灵活性,正在改变 UI 开发的传统范例。 服务器驱动 UI 不仅仅是…...
Postman如何做接口自动化测试?
前言 什么是自动化测试 把人对软件的测试行为转化为由机器执行测试行为的一种实践。 例如GUI自动化测试,模拟人去操作软件界面,把人从简单重复的劳动中解放出来。 本质是用代码去测试另一段代码,属于一种软件开发工作,已经开发完…...
excel文本函数篇2
本期主要介绍LEN、FIND、SEARCH以及后面加B的情况: (1)后缀没有B:一个字节代表一个中文字符 (2)后缀有B:两个字节代表一个中文字符 1、LEN(text):返回文本字符串中的字符个数 2、…...
【MyBatis】动态SQL > 重点:${...}和#{...}与resultMap和resultType的区别
目录 一、MyBatis动态sql 1.1 动态sql的作用 1.2 动态sql作用论证 1.2.1 条件判断:<if> 1.2.2 循环迭代:<foreach> 1.2.3 SQL片段重用 1.2.4 动态条件组合:<choose><when><otherwise> 1.2.5 <where…...
什么是BEM命名规范?为什么要使用BEM命名规范?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ BEM命名规范⭐ 为什么使用BEM命名规范?⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为…...
JavaScript:交集和差集的应用场景
在集合A和集合B中,属于集合A,同时也属于集合B的元素组成的集合,就是交集。 在A中所有不属于集合B元素,组合成集合,就是差集。 那么在平时的开发中,如何使用差集和交集来解决问题呢? 现在有这…...
达梦数据库表空间创建和管理
概述 本文将介绍在达梦数据库如何创建和管理表空间。 1.创建表空间 1.1表空间个数限制 理论上最多允许有65535个表空间,但用户允许创建的表空间 ID 取值范围为0~32767, 超过 32767 的只允许系统使用,ID 由系统自动分配,ID不能…...
三、MySQL 数据库安装集
一、CentOS—YUM 1. MySQL—卸载 # 1、查看存在的MySQL。 rpm -qa | grep -i mysql rpm -qa | grep mysql# 2、删除存在的MySQL。 rpm -e –-nodeps 包名# 3、查找存在的MySQL目录。 find / -name mysql# 4、删除存在的MySQL目录。 rm -rf 目录# 5、删除存在的MySQL配置文件。…...
【BASH】回顾与知识点梳理(三十九)
【BASH】回顾与知识点梳理 三十九 三十九. make、tarball、函数库及软件校验39.1 用 make 进行宏编译为什么要用 makemakefile 的基本语法与变量 39.2 Tarball 的管理与建议使用原始码管理软件所需要的基础软件Tarball 安装的基本步骤一般 Tarball 软件安装的建议事项 (如何移除…...
医疗网站建设行业现状/seo整站优化报价
找到所有数组中消失的数字 题目描述: 给定一个范围在 1 ≤ a[i] ≤ n ( n 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。 找到所有在 [1, n] 范围之间没有出现在数组中的数字。 您能在不使用额外空间且时间复杂度…...
南京网络营销课程培训/西安seo外包服务
梁老师今天主要讲解如何调用工作流API来访问工作流,包括如下一些引伸知识:1. 定义期 :也叫建模期。 按着建模规则生成一份过程定义。 运行态: 根据定义,执行服务。 2. 组织模型:把任务分给谁。3. 三种…...
一个教做网页的网站/学校网站模板
1.JDK,JRE,JVM的作用及关系 (1)作用 JVM:保证Java语言跨平台 JRE:Java程序的运行环境 JDK:Java程序的开发环境 (2)关系 JDK:JRE工具 JRE:JVM类库 …...
深圳微信网站制作/电商网站建设定制
赛后总结: 先把所有的可做题放上线程,debug在线程上有题的时候永远下机找 宁愿机下多线程debug不要机下多线程空想题 A. 0:05:54 solved by hl 温暖的签到 #include <map> #include <set> #include <ctime> #include <cmath> #include <…...
佛山网站建设推广/媒体宣传推广方案
大学一毕业进入职场,职场跟学校是不一样的,你不去问问题什么的,或者请教别人也不会告诉你,问多了别人还会烦躁,甚至还会觉得你没有能力,但是别人告诉你也是留一手,怕你学会抢他饭碗。牵引力教育…...
wordpress批量发布文章/百度指数查询官方下载
四 管理项目知识 1.Inputs 项目管理计划: 项目管理计划的任何组件都可用作本过程的输入 项目文件: 经验教训登记册 项目团队派工单 资源分解结构 相关方登记册 可交付成果: 可交付成果是在某一过程、阶段或项目完成时,必须产生的任何独特并可…...