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

什么是优秀的单元测试?

阅读本文之前,请投票支持这款 全新设计的脚手架 ,让 Java 再次伟大!

单元测试的质量意义

合理编写单元测试,可使团队工程师告别牛仔式编程,产出易维护的高质量代码。随着单元测试覆盖率的上升,项目会更加的健壮,团队的信心满满,充满斗志。

无论是瀑布团队还是敏捷团队,单元测试及自动化单元测试作为重要的质量保证手段的价值已经被大家接纳与认可。

但是,对于大多数团队来讲当测试覆盖率提升一定阶段后,收益会迎来瓶颈。

单元测试的设计意义

单元测试除了作为质量保证手段以外,更应该作为设计手段。TDD 的核心思想之一,就是使用单元测试作为设计手段,使开发者在编写测试用例时,以调用者的思维方式来写代码,这种方式的收益会比作为保护手段更高。
利用测试驱动开发可以让你的设计更加优良、编写出可测试的代码、还能避免在不切实际的假设上过度设计系统。

优秀测试的共性

  1. 良好的测试代码具备可读性和可维护性。
  2. 测试代码在项目中有结构化的组织方式。
  3. 测试具备可靠性和可重复性。
  4. 合理的使用测试替身。
  5. AAA(Arrange, Act & Assert)——准备、执行、断言。 编写测试代码时,使用被称为三A模型:准备、执行、断言。

测试替身(test double)

为了方便的测试代码以及绕开各种环境限制,应该使用替身来代替真实对象。

public class Car {// 对外封闭的引擎对象。你无法知道内在信息。private Engine engine;public Car(Engine engine) {this.engine = engine;}// 需要测试的启动功能public void start() {engine.start();}// 需要测试汽车的驾驶功能public void drive(Route route) {// 根据路线状态获取提供给 car 使用的各种方向 (十分复杂的算法,初始化时需要涉及 gis 算法。最终获取的对对象与当前的时间有关)for (Directions directions : route.getDirections()) {directions.follow();}}}

为了验证一段代码的行为符合你的期望,最好的选择是替换其周围的代码,使你获得对环境的完整控制。使用了替身,能让执行速度变得更快,并且可以随意模拟特殊情况。


public class TestRoute extends Route {// 伪造的替身实现了快速返回路线以及返回固定路线的方法,使测试具备了可靠与可重复性@Overridepublic List<Directions> getDirections() {System.out.println("快速的返回固定的路线");return new ArrayList<Directions>();}}public class TestEngine extends Engine {private boolean engineFlg = false;@Overridepublic void start() {engineFlg = true;}@Overridepublic void stop() {engineFlg = false;}public boolean isEngineFlg() {return engineFlg;}public void setEngineFlg(boolean engineFlg) {this.engineFlg = engineFlg;}}

使用替身替换协作者,隔离被测代码意味着将需要测试的代码与其他代码隔离开来。

    @Testpublic void testCarStart() {TestEngine engine = new TestEngine();new Car(engine).start();assertTrue(engine.isEngineFlg());}@Testpublic void testCarDrive() {TestRoute testRoute = new TestRoute();TestEngine engine = new TestEngine();new Car(engine).drive(testRoute);}

测试替身的类型

替身拥有多种类型。不同的测试场景选择合理的测试替身是优秀测试的必要条件之一。测试替身的类型也决定了测试类的命名方法,尤其特别注意。

桩(stub)

用最简单的可能实现来代替真实实现。桩总是短小的。最好的例子就是一个对象的所有方法都只有一行,并且返回一个默认值。

public class LoggerStub implements Logger {@Overridepublic void log() {// Nope}
}
伪造对象 (fake)

有时,我们至少需要填充一些行为,而有时候你需要测试替身根据收到的消息种类来表现出不同的行为。
Fake 就像是真实事物的简单版本,他能够伪造真实事物的行为,具备简单的业务逻辑,同时没有副作用或者使用真实事物的其他后果。

    public class TestRoute extends Route {// 伪造的替身实现了快速返回路线以及返回固定路线的方法,使测试具备了可靠与可重复性@Overridepublic List<Directions> getDirections() {System.out.println("快速的返回固定的路线");return new ArrayList<Directions>();}}

伪造对象与测试桩十分常用,你可以在测试时用它们替换掉缓慢的真实事物,以及鞭长莫及的依赖。

测试间谍 (spy)

测试间谍用于记录你和某个不对你开放的对象之间的交互。当对象与协作者之间交互时,无法获取协作者交互结果的情况下,会使用测试间谍。

// 测试间谍public class TestEngine extends Engine {// 对外暴露信息private boolean engineFlg = false;// 比真实对象的 start 方法更具有交互性的方法。@Overridepublic void start() {engineFlg = true;}@Overridepublic void stop() {engineFlg = false;}// 对外提供用于获取交互结果的接口,用于断言。public boolean isEngineFlg() {return engineFlg;}public void setEngineFlg(boolean engineFlg) {this.engineFlg = engineFlg;}}
模拟对象 mock

模拟对象是在一个特定情境下可配置行为的对象。模拟对象时一种更加高级与特殊的测试间谍,它关注的重点是两个对象之间的交互行为。

public void mockTest(){final Internet internet = context.mock(Internet.class);context.checking(new Expectations() {{one(internet).get(with(containsString("langpair=en")));will(returnValue("ok"))}};Translator t = new Translator(internet);String translation = t.translate("flower",ENGLISH);assertEquals("ok",translation)}

通过使用模拟对象的预测返回值行为,我们可以更加精确的对交互行为进行验证。

如何选择合适的替身类型

  • 如果你测试的重点是交互,即两个对象之前的调用,你可能需要一个模拟对象 mock。
  • 如果你决定使用 Mock ,但测试代码最终看起来不像你想象的那么漂亮了,则考虑使用 Spy。
  • 如果你只关心协作对象向被测对象输送的响应,用 stub 解决问题。
  • 如果你运行的是一个复杂场景,其中它所依赖的服务无法供测试使用,但是你又需要这些服务提供的行为哪怕是一个简单的提示,那就应该考虑使用 Fake。

相关文章:

什么是优秀的单元测试?

阅读本文之前&#xff0c;请投票支持这款 全新设计的脚手架 &#xff0c;让 Java 再次伟大&#xff01; 单元测试的质量意义 合理编写单元测试&#xff0c;可使团队工程师告别牛仔式编程&#xff0c;产出易维护的高质量代码。随着单元测试覆盖率的上升&#xff0c;项目会更加…...

服务器安装Anaconda,Anaconda安装Pytorch

1.服务器安装Anaconda 1.1 下载Anaconda 在服务器上直接下载 wget https://repo.anaconda.com/archive/Anaconda3-2024.06-1-Linux-x86_64.sh1.2 安装Anaconda bash Anaconda3-2024.06-1-Linux-x86_64.sh然后就显示下面&#xff1a;more 安装过程一直enter即可&#xff0c;…...

YOLO目标检测理论详解,YOLOv1理论知识讲解,超w字精读(学习YOLO框架必备),全网最详细教程

文章目录 前言一、目标检测理论1.目标检测发展史2.目标检测框架理论基础3.本章小结 二、YOLOv1理论知识1.YOLOv1网络结构2.YOLOv1检测原理3.YOLOv1的训练流程&#xff08;1&#xff09;边界框的位置参数&#xff08;2&#xff09;边界框的置信度&#xff08;3&#xff09;类别置…...

SpringBoot3.x和OCR构建车牌识别系统

本专题旨在展示 OCR 技术与 SpringBoot3.x 框架结合的广泛应用。我们会深入探讨它在医疗、金融、教育、交通、零售、公安等多个领域的现实应用。每个应用场景都会提供详细的实例、面临问题的分析与解决策略&#xff0c;以帮助您深入理解 OCR 技术在实践中的关键作用。让我们一同…...

conda 容器学习笔记之一 -- 基础环境配置

1、容器瘦身导致部分应用缺少&#xff0c;需要在非容器环境下部署环境。但为避免破坏现有环境&#xff0c;现有使用conda环境进行隔离管理 创建&#xff1a;conda create -n tts python3.10.0 2、conda 是python环境管理&#xff0c;和python无关的东西比如cann还是会影响 下载…...

Oracle分区表改造(三):通过分区交换和分裂改造为分区表

Oracle分区表改造(三):通过分区交换和分裂改造为分区表 源表数据准备范围分区表改造:非间隔分区创建普通分区表分区交换分区分裂范围分区表改造:间隔分区创建间隔分区表分区交换分区分裂表重命名🐬 创建只有一个分区的分区表, 通过分区交换将原表变成分区表,然后分裂分…...

LeetCode 0908.最小差值 I:思维(遍历)

【LetMeFly】908.最小差值 I&#xff1a;思维&#xff08;遍历&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/smallest-range-i/ 给你一个整数数组 nums&#xff0c;和一个整数 k 。 在一个操作中&#xff0c;您可以选择 0 < i < nums.length 的…...

Python基础之循环语句

在Python的编程世界里&#xff0c;循环结构犹如一把神奇的钥匙&#xff0c;开启高效处理数据和重复执行任务的大门。它赋予程序员强大的力量&#xff0c;让代码充满活力。Python主要有两种类型的循环语句&#xff1a;for循环和while循环。 一、for循环 for循环通常用于遍历一个…...

项目管理软件真的能让敏捷开发变得更简单吗?

敏捷开发是一种以快速交付和适应变化为核心特点的软件开发方法。其特点包括尽早并持续交付、能够驾驭需求变化、版本周期内尽量不加任务、业务与开发协同工作、以人为核心、团队配置敏捷等。 例如&#xff0c;尽早并持续交付可使用的软件&#xff0c;使客户能够更早地体验产品…...

互联网名称之时间戳

什么是时间戳 时间戳&#xff08;Timestamp&#xff09;是一种用于表示特定时刻的数值或字符串&#xff0c;通常以日期和时间的形式出现。它用于记录某一事件发生的准确时间&#xff0c;在计算机系统中常被用于日志记录、数据处理和同步等场景。 常见的时间戳 在互联网中常见…...

Leetcode—1242. 多线程网页爬虫【中等】Plus(多线程)

2024每日刷题&#xff08;187&#xff09; Leetcode—1242. 多线程网页爬虫 实现代码 /*** // This is the HtmlParsers API interface.* // You should not implement it, or speculate about its implementation* class HtmlParser {* public:* vector<string>…...

RISC-V笔记——内存模型总结

1 前言 Memory consistency model定义了使用Shared memory(共享内存)执行多线程(Multithread)程序所允许的行为规范。RISC-V使用的内存模型是RVWMO(RISC-V Weak Memory Ordering)&#xff0c;RVWMO内存模型是根据全局内存顺序(global memory order)定义的&#xff0c;全局内存…...

后端常用安全措施

一、限流 1.简介 限流就是限制流量&#xff0c;但这里的流量是一个比较笼统的概念。如果考虑各种不同的场景&#xff0c;限流是非常复杂的&#xff0c;而且和具体的业务规则密切相关 通过限流&#xff0c;可以控制服务请求的速率&#xff0c;从而提高系统应对突发大流量的能…...

虚拟机数据恢复—通过拼接数据库页碎片的方式恢复数据库的数据恢复案例

虚拟机数据恢复环境&#xff1a; 某品牌服务器通过同品牌某型号的RAID卡&#xff0c;将4块STAT硬盘为一组RAID10阵列。上层部署XenServer虚拟化平台&#xff0c;虚拟机安装Windows Server系统&#xff0c;每台虚拟机有两个虚拟机磁盘&#xff08;系统盘 数据盘&#xff09;&am…...

【vue】自封组件,基于vue2封装一个弹框组件

源码&#xff1a;https://download.csdn.net/download/galaxyJING/89913551...

ES6基础知识

一、定义变量的关键字let和const 1. let 定义变量的语法&#xff1a; let 变量名 值; 2. 和var定义变量的区别 1. 是否支持同一个作用域变量同名 var支持&#xff0c;let不支持 2. 是否支持预解析 var支持&#xff0c;let不支持 3. 是否会挂载在window对象…...

基于Multisim的模拟拔河游戏比赛设计与仿真

1.设计一个模拟拔河游戏比赛的逻辑电路 2.使用15个发光二极管表示绳子&#xff0c;开机后只有最中间的发光二极管亮。 3.比赛双方各持一个按钮&#xff0c;快速不断地按动按钮&#xff0c;产生脉冲&#xff0c;谁按的快&#xff0c;发光的二极管就向谁的方向移动&#xff0c;每…...

MyBatis 配置详解

在项目中经常会用到 mybatis 相关的一些配置&#xff0c;而在启动类项目工程中&#xff0c;一般会把 mybatis 配置文件单独写到 mybatis,yml 中&#xff0c;如下简单介绍下常用的 mybatis 配置 mybatis:configuration:call-setters-on-nulls: truemap-underscore-to-camel-case…...

研发运营一体化(DevOps)能力成熟度模型

目录 应用设计 安全风险管理 技术运 持续交付 敏捷开发管理 基于微服务的端到端持续交付流水线案例 应用设计 安全风险管理 技术运 持续交付...

躺平成长-利用kimi编辑助手帮助自己编程第二天

天有道&#xff0c;无常道&#xff0c;兵无常势。 {今天开始听歌&#xff08;歌曲&#xff1a;青丝&#xff01;&#xff09;进行编程&#xff01;} 尝试用ai帮助自己进行小程序的开发&#xff0c;同时最为关键&#xff0c;是无法能够完成相关的代码的记忆&#xff0c;所以我开…...

OpenSuse-搭建NFS-Server

在OpenSUSE上搭建NFS服务可以通过以下步骤完成&#xff1a; ### 1. 安装NFS服务器软件 首先&#xff0c;确保你已经安装了NFS服务器软件包。你可以使用zypper命令来安装&#xff1a; sudo zypper install nfs-kernel-server### 2. 配置NFS导出目录 编辑/etc/exports文件&#x…...

【数据结构与算法】之二分查找

二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的搜索算法。它通过比较数组中间元素与目标值来工作&#xff0c;从而将搜索范围缩小到一半&#xff0c;也称折半查找&#xff0c;是一种非常高效的工作于有序数组的查找算法。本文主要介绍二分查找…...

vue修饰符

表单修饰符 1、lazy <input type "text" v-model.lazy "value"> <p>{{value}}</p>lazy跟懒加载类似&#xff0c;只有再说鼠标离开光标的时候才会触发&#xff0c;也就是说在input事件的oninput书法的时候不会赋值&#xff0c;当onch…...

Oracle里面,with ... as 用法介绍

在Oracle数据库中&#xff0c;WITH AS 子句&#xff08;也称为公用表表达式&#xff0c;CTE, Common Table Expression&#xff09;是一种在查询中定义临时结果集的方法。这个临时结果集可以在后续的查询中被引用&#xff0c;就像是一个临时的表或视图一样。使用 WITH AS 子句可…...

一个简单的Qt Console Application计算练习程序

初步体验Qt Creator 用途&#xff1a;练习20以内2位数乘法速算的程序 功能1&#xff1a;支持用户设定题目数量 std::cout << "请输入本次练习题目数量&#xff1a;";int numProblems 0;std::string num;std::cin >> num;try {numProblems std::stoi(…...

windows文件拷贝给wsl2的Ubuntu

参考&#xff1a; windows文件如何直接拖拽到wsl中_win 移到文件到wsl-CSDN博客 cp -r /mnt/盘名/目标文件 要复制到wsl中的位置e.g.cp -r /mnt/d/byt5 /home Linux文件复制、移动、删除等操作命令_linux移动命令-CSDN博客 Linux 文件、文件夹的复制、移动、删除 - Be-myse…...

vivado 采用 SSI 器件进行设计

SSI 管脚的考虑因素 在为特定 SLR 中的组件规划管脚时&#xff0c;请将引脚放置在同一个 SLR 中。例如&#xff0c;将器件的 DNA 信息作为外部接口的一部分 时&#xff0c;请将该接口的引脚放置在 DNA_PORT 所在的主 SLR 中。其它考虑因素包括如下&#xff1a; • 把…...

Lua环境安装

软考鸭微信小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 Lua是一种轻量级、小巧且易于嵌入应用程序的脚本语言&#xff0c;广泛用于游戏开发、Web开发、自动化脚本等领域。本文将详细介绍如何在不同操作系统上安装L…...

浏览器控制的无线开关

esp32-c3 作为HTTP server 控制led 灯。服务器注册两个uri 。一个"/open" 控制开&#xff0c;一个"/close"控制关。下一步再用一片c3作为客户端&#xff0c;运行http client 发送/open. /Close 模拟浏览器&#xff0c;控制led. 其实只要用手机或pc或平…...

Docker部署SSM项目及避坑指南

#又踩坑了&#xff0c;这里记录一下&#xff0c;以免日后忘记 前言&#xff1a;本来以为用docker部署个项目很轻松&#xff0c;嗯结果&#xff0c;又踩坑了&#xff0c;这里记录一个完整版。话不多说&#xff0c;开整。 第一步&#xff1a; 用docker拉取MySQL和Tomcat&#…...