当前位置: 首页 > news >正文

软件测试技术之单元测试—工程师 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)

怎么写单元测试&#xff1f; JUnit 简介 基本上每种语言和框架都有不错的单元测试框架和工具&#xff0c;例如 Java 的 JUnit、Scala 的 ScalaTest、Python的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的&#xff0c;我们下面就简单介绍下 JUnit。 JUnit…...

项目中超图 for openlayer和超图for cesium同时引入的问题

一个项目中同时用到了超图的openlayer和cesium版本&#xff0c;首先我是外部引入的超图的开发包&#xff0c;你要是通过npm导入的那就没关系了。 <script type"text/javascript" src"/static/openlayer/supermap/ol/iclient-ol.min.js"></script&…...

3D与沉浸式技术,如何助力企业数字化转型?

说起3D&#xff0c;估计许多读者朋友会在第一时间想起《阿凡达》系列和《侏罗纪公园》系列电影大作。每一帧细节纤毫毕现的逼真画面&#xff0c;让观众几乎分不清虚拟与现实&#xff0c;完全沉浸在导演打造的视觉盛宴中。 事实上&#xff0c;除了大家所熟知的3D影视动画之外&am…...

excel vba 将多张数据表的内容合并到一张数据表

功能描述&#xff1a; 一个Excel文件有很多个 样式相同 的数据表&#xff0c; 需要将多张数据表的内容合并到一张数据表里。 vba实现代码如下&#xff1a; Attribute VB_Name "NewMacros" Option Explicit Public Const Const_OutSheetName As String "V…...

接口和抽象类的区别?解析接口和抽象类的特点和用法

接口和抽象类的区别&#xff1f;解析接口和抽象类的特点和用法 引言 在面向对象编程中&#xff0c;接口和抽象类是两个非常重要的概念。它们都可以用于定义一组相关的方法&#xff0c;但在实际使用中有一些差异。本文将探讨接口和抽象类的区别&#xff0c;并通过示例代码和测…...

vscode-vue项目格式化

一、插件要求 Prettier Vetur 二、配置文件 {"workbench.startupEditor": "newUntitledFile","files.autoSave": "off", // 关闭文件自动保存&#xff0c;避免开发时候页面变化"editor.tabSize": 2, // tab距离"ve…...

SAP MM学习笔记26- SAP中 振替转记(转移过账)和 在库转送(库存转储)1- 移动Type间振替转记

SAP 中在库移动 不仅有入库&#xff08;GR&#xff09;&#xff0c;出库&#xff08;GI&#xff09;&#xff0c;也可以是单纯内部的转记或转送。 1&#xff0c;振替转记&#xff08;转移过账&#xff09; 2&#xff0c;在库转送&#xff08;库存转储&#xff09; 1&#xff…...

SAP SPL(Special Ledger)之注释行项目-Noted Items

财务凭证过账里常见的SPL特殊总账标识根据业务主要有三种&#xff0c;BoE-billing of exchange: 汇票业务&#xff0c;包括商业汇票和银行汇票&#xff1b;Down Payment&#xff0c;预付款业务&#xff0c;包括供应商和客户预付款和申请&#xff1b;其它&#xff0c;一般是保证…...

学习平台助力职场发展与提升

近年来&#xff0c;随着互联网技术的发展&#xff0c;学习平台逐渐成为了职场发展和提升的必备工具。学习平台通过提供丰富的课程内容、灵活的学习时间和个性化的学习路径&#xff0c;帮助职场人士更好地提升自己的技能和知识储备&#xff0c;为职场发展打下坚实的基础。 学习…...

有没有免费格式转换工具推荐?PDF转化为PPT的方法

在当今职场生活中&#xff0c;掌握文件格式转换技能变得异常重要。将PDF文档转换为PPT格式可以在演讲、报告等场合更好地展示和传达信息&#xff0c;为我们的专业形象增添亮点&#xff0c;接下来我们可以一起来看一下“有没有免费格式转换工具推荐?PDF转化为PPT的方法”相关的…...

【LeetCode-经典面试150题-day12】

20.有效的括号 题意&#xff1a; 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括…...

TCP机制-延迟应答,捎带应答

在看本篇博客前推荐先看TCP中窗口和滑动窗口的含义以及流量控制 延迟应答和捎带应答都是TCP用于提高网络传输效率的机制 延迟应答 当发送端发送数据给接收端了以后&#xff0c;按道理接收端的内核会立即返回ACK&#xff08;应答报文&#xff09;给发送端&#xff0c;而且ACK&a…...

【Redis从头学-8】Redis中的ZSet数据类型实战场景之用户积分榜

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;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 各个服务之间的数据库都是相互独立的&#xff0c;你不能直接访问对方的数据库&#xff0c;只能从一个服务像另外一个服务发起远程调用 在订单模块的服务中 …...

基于Android R快速编译recovery-ramdisk.img

Android默认没有单编recovery-ramdisk.img的命令&#xff0c;我们可以自己修改Makefile实现 修改&#xff1a;build/core/Makefile 添加&#xff1a; .PHONY: recovery-ramdisk-nodeps recovery-ramdisk-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)echo "make …...

Redis分布式缓存

分布式缓存 -- 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题&#xff1a; 1.Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化 AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#x…...

最大公约数和最小公倍数

最大公约数&#xff1a; 概念&#xff1a; 公约数中最大的称为最大公约数。 对任意的若干个正整数&#xff0c;1总是它们的公因数。 公约数与公倍数相反&#xff0c;就是既是A的约数同时也是B的约数的数&#xff0c;12和15的公约数有1&#xff0c;3&#xff0c;最大公约数就是…...

数据结构——二叉搜索树(附带C++实现版本)

文章目录 二叉搜索树概念 二叉树的实际应用二叉树模拟实现存储结构二叉搜索树构成二叉搜索树的查找插入操作中序遍历二叉树的删除循环(利用左子树最右节点&#xff09;递归(利用右子树根节点) 二叉树拷贝二叉树资源的销毁 二叉树实现完整代码总结 二叉搜索树 概念 二叉搜索树…...

C++(3)C++对C的扩展Extension

类型增强 1、类型更加严格 不初始化&#xff0c;无法通过编译&#xff1b;C不初始化&#xff0c;则随机赋值 #include <iostream> #include <stdlib.h>int main() {const int a 100; //真正的const,无法修改 // int *p &a; 报错const int *p…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...