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

如何聪明地编写测试

作者|Maxim Schepelin

Booking公司软件开发工程师

编译整理|TesterHome

以下为作者观点:

在我(作者)的职业生涯中,我多次看到团队如何开始自动化测试,当然并非所有尝试都成功。在这篇文章中,我将分享一些关于在团队中创建自动化测试文化以及塑造从零测试到不同级别的可靠测试集过程中一些技巧。

一些团队进行自动化测试的常见方式是设定一个目标,例如:“在本季度,我们将把测试覆盖率提高到X%”。

我认为这是提高质量的次优方法。因为,我们最终的目标并不是测试覆盖的行数百分比。我们的目标是,在代码库的整个生命周期内,对代码所做的新修改形成快速反馈回路

打个比方,在身材管理上,我们的目标不应该只是在这个季度锻炼身体,以迎接下一个海滩季节,我们的目标应该是在余生都保持良好的体型。

同样,我认为自动测试也是团队遵循的一套习惯,而保持新习惯的一个重要方面就是尽早发现新行为的好处,下面就是我认为应该做的事。

了解代码库的预期寿命

你的团队当前正在开发的代码库的预期寿命是多少?你期望代码在生产环境中保留多久?这些问题可能看起来与测试的讨论无关,但它们很重要。

这是因为代码库的生命周期将定义你期望发生更改的位置。根据我的经验,假设业务应用程序的寿命为5到7年是相当合理的。有时,甚至长达10年以上。现在想想10年后可能会发生什么变化:

  • 硬件代码运行于...

  • 操作系统

  • 软件所依赖的所有技术都将获得重大更新

  • 所有库和框架都将更新

  • 语言版本

  • 作业工具

  • 构建过程

  • 部署流程

上面这个列表并不完整,甚至没有涵盖由于新要求而对代码本身所做的任何更改。

你可能会看到,目前很多流行的开源项目已经将这些变化考虑在内的,因此它们正在不同的操作系统版本和不同的硬件上进行测试,作为其CI管道的一部分。这对于它们的寿命来说是完全合理的。另一个例子可能是为代码严重依赖的技术编写测试。不是因为您想测试其他人的代码,而是因为您希望在将 Kafka/Postgres/其他版本更新到下一个主要版本时拥有一个安全网。

这里的目的是让团队思考他们实际上应该测试什么。现在,让我们更接近实际测试。想象一下,你手上有了一个代码库,并且它还没测试过。你会从哪里开始编写测试呢?

识别经常变化的“热点”

从零测试过渡到测试良好的代码库需要团队付出大量努力。因此,尽快获得自动化测试的好处非常重要。

我曾见过一种反复出现的模式:一个团队设定了一个目标,要达到70%的代码行覆盖率。过了一段时间,当他们达到这个目标后,团队仍然感觉不到他们编写的测试有任何价值:上线后的缺陷率仍然很高,上线成功率仍然不变。最终,团队会对自动化测试产生怨恨……"我们已经试过了,它对我们团队不起作用"。

我理解一些人放弃的原因。他们不得不挤出时间编写测试,与产品负责人协商技术债务问题,并投入了大量精力。然而,他们并没有从这些投入中得到什么。

另一种方法是找出变更发生频率较高的地方,并开始编写涵盖这些热点的测试。请记住,自动测试的最终目标是为新变化提供快速反馈回路。因此,值得考虑变化发生的位置。幸运的是,有这样一个工具--"git-extras "软件包中的 "git effort"。下面是它的工作原理:


it effort **/*.go --above 10 path commits active days assert/assertions.go........................ 223 163 assert/assertions_test.go................... 145 108 mock/mock.go................................ 106 81 mock/mock_test.go........................... 62 54 suite/suite.go.............................. 46 37 require/require.go.......................... 45 39 assert/assertion_forward.go................. 44 38 require/require_forward.go.................. 43 37 assert/assertion_format.go.................. 36 31 suite/suite_test.go......................... 34 26 assert/doc.go............................... 19 17 assert/http_assertions.go................... 18 15 assert/forward_assertions_test.go........... 17 17 require/requirements.go..................... 16 15 assert/forward_assertions.go................ 16 16 assert/http_assertions_test.go.............. 15 9 assert/assertion_order.go................... 11 10 _codegen/main.go............................ 11 10

这是一个开源项目的例子。它显示了每个文件收到的提交次数,以及每个文件的活跃开发天数。从上面的例子中,你可能会发现更改频率并不是平均分布的。有些文件几乎每天都会更改,而有些文件每年才更改几次。

这就是为什么代码覆盖率是一个误导性指标的原因之一:它忽略了更改频率。你可能会获得很高的测试覆盖率,但如果热点仍未被发现,你将一无所获。

因此,请确保首先覆盖经常更改的测试代码。这样,你的团队就会更快地注意到自动化测试的好处。

这种策略可以帮助你选择从哪里开始——但要维持新习惯还需要做更多的工作。那么,我们来谈谈如何确保你的测试确实可以帮助你验证传入的更改。

在与几个团队合作,从没有测试到实现良好的测试自动化的过程中,我注意到,在一开始,人们认为测试是二元对立的:"我们有 X 的测试 "或 "我们没有 X 的测试"。

后来,人们开始问自己的测试有多好。同样,我认为这是对测试覆盖率指标的不良影响,因为测试覆盖率指标只能反映测试用例是否覆盖了某一行代码。但我们不要把这一点与代码执行中可能出现的逻辑分支或输入参数的变化混为一谈。

我将使用 "测试完整性 "来描述测试覆盖所有可能执行路径的程度。为了说明代码覆盖率和测试完备性之间的区别,我们来看下一个例子:

我有一个简单的函数,可以将两个数字相加:

func Sum(a, b int) int {    return a + b}

要实现该函数100%的代码覆盖率,只需编写一个调用该函数的测试即可,测试中不需要任何参数。assert.Equal(Sum(1, 2), 3)`。100% 的代码覆盖率!但这能覆盖所有可能的情况吗?

显然没有,根据输入参数的不同,可能会有更多的变化:

# these are just basic school mathassert.Equal(Sum(1, 2), 3)assert.Equal(Sum(1, 0), 1)assert.Equal(Sum(3, -1), 2)assert.Equal(Sum(-1, -1), -2)assert.Equal(Sum(-1, 0), -1)
# there are more language specific test-casesassert.Equal(Sum(math.MaxInt, 1), math.MinInt)assert.Equal(Sum(math.MinInt, -1), math.MaxInt)

因此,与其询问某件事情是否有测试,不如询问这些测试场景有多完整。


将两个数字相加的例子可能看起来有些矫揉造作,但根据研究 "简单测试可防止大多数关键故障",研究表明:

  • 几乎所有灾难性故障(92%)都是由于软件中明确提示的非致命错误处理不当造成的。

  • 大多数生产故障(77%)可以通过单元测试重现

作为工程师,我们在考虑可能出错的情况并设计测试用例以应对这些故障时,应保持适当的偏执。

结论

设定一个可衡量的目标,以了解采用自动化测试的进展情况,这可能很有诱惑力。然而,这可能会导致你达到了目标,却没有实现目标。

另一种方法是不追求行覆盖率指标,而是确保经常更改的代码具有良好的测试完整性。

这样,当团队对经常变化的代码有了一个安全网后,他们就可以专注于从自动化测试中获得真正的价值:

  • 是否可以通过增加更多测试来提高部署成功率?

  • 能否通过自动化测试而不是手动步骤重现错误?

  • 下一次对依赖项进行重大版本更新时,我们如何才能更早地发现问题?


对这些问题的回答可能不会产生容易衡量的目标,但它会为您制定更有意义的测试策略。(原文链接:https://medium.com/booking-com-development/write-tests-smarter-not-harder-fb49c7ab89fd)

相关文章:

如何聪明地编写测试

作者|Maxim Schepelin Booking公司软件开发工程师 编译整理|TesterHome 以下为作者观点: 在我(作者)的职业生涯中,我多次看到团队如何开始自动化测试,当然并非所有尝试都成功。在这篇文章中…...

CF1866M Mighty Rock Tower

CF题面 luogu题面 期望题。 题目大意:一个人在搭积木,每次堆叠可能成功或失败,失败可能导致其下面连续的若干块掉落。给定搭每一块时失败的概率,求堆叠完成的期望次数。 具体的,假设当前正在堆叠第 i 块,…...

【漏洞复现】Apache_Tomcat7+ 弱口令 后台getshell漏洞

感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 说明内容漏洞编号漏洞名称Tomcat7 弱口令 && 后台getshell漏洞漏洞评级高…...

2023-11-7 OpenAI 45 分钟发布会:演示关于 GPT Store 构建 GPT、零代码创建 AI Agent

本心、输入输出、结果 文章目录 2023-11-7 OpenAI 45 分钟发布会:演示关于 GPT Store 构建 GPT、零代码创建 AI Agent前言Sam Altman 正在创建一个「创业导师 GPT」零代码创建 AI AgentAssistants API 封装的能力包括 Sam Altman 对发布会总结相关链接弘扬爱国精神 …...

嵌入式系统中的FPGA

举个栗子 假设你有一台智能家居系统,其中的FPGA可以被类比为智能家居中的中央控制器。 智能家居系统: 定制家居逻辑: 你希望智能家居系统能够根据你的生活习惯、时间表和喜好自动控制灯光、温度、窗帘等设备。就像FPGA中可以根据需求重新配置…...

String,StringBulider,StringBuffer的简单说明

目录 1.String 2.StringBuffer 3.StringBuilder 4.线程安全的验证 1.String String是声明在java.lang下的一个类。 String被定义为final,表示不能被继承。内部定义了final char value[]用于存储字符串数据,所以String对象的值是不可改变的。每次对S…...

Python编程题集(第一部分基本语法基础)

Demo01 摄氏温度转化为华氏温度 题目描述: 输入一个摄氏温度的值,将它转变为华氏温度,并将结果输出 转换的公式为如下:fahrenheit (9 / 5 ) * celsius 32 输入输出描述 输入一个值表示摄氏温度celsius 输出华氏温度fahren…...

手动关闭PS中的TopazStudio2的登录窗口

2021 adobe photoshop Topaz Studio 2 不是使用防火墙出站规则,是手动关闭的解决方案 点击社区-切换用户,登录窗口会出现X,可以手动关闭...

[elastic 8.x]java客户端连接elasticsearch与操作索引与文档

初始化客户端 引入相关依赖 <dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.10.2</version> </dependency>初始化客户端 为了方便演示&#xff0c;我关闭了ela…...

接口测试总结

本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什么要做&#xff1f; 第二部分&#xff1…...

计算机基础知识44

overflow溢出属性 visible&#xff1a;默认值&#xff0c;内容不会被修剪&#xff0c;会呈现在元素框之外。hidden&#xff1a;内容会被修剪&#xff0c;并且其余内容是不可见的。scroll&#xff1a;内容会被修剪&#xff0c;但是浏览器会显示滚动条以便查看其余的内容。auto: …...

【StringBuilder和StringBuffer】

文章目录 StringBuilder和StringBufferString类、StringBuilder和StringBuffer的区别 StringBuilder和StringBuffer的区别StringBuilder 字符串逆置 StringBuilder和StringBuffer String类、StringBuilder和StringBuffer的区别 String类的特点是不可变性&#xff0c;所以Stri…...

用java代码实现security

Java中security的实现主要涉及到以下几个方面&#xff1a; 认证(Authentication) 认证是确认用户身份的过程&#xff0c;Java中提供了不同的认证机制来保护应用程序不被未授权的用户访问。常用的认证机制有以下几种&#xff1a; 基于口令的认证&#xff1a;要求用户输入用户名…...

【Java 进阶篇】Java Session 原理及快速入门

大家好&#xff0c;欢迎来到本篇博客。今天&#xff0c;我们将探讨Java Web开发中一个重要而令人兴奋的概念&#xff0c;即Session&#xff08;会话&#xff09;。Session是一种在Web应用程序中跟踪用户状态和数据的机制。我们将深入了解Session的原理&#xff0c;并通过示例来…...

MoveFunsDAO 星航计划|从Move入门Web3与深入实践「公益课堂」

Move 语言作为最安全的编程语言之一&#xff0c;在资产的安全性和保护方面有着显著优势&#xff0c;被寄予引领 Web3 世界的全新叙事的厚望。 随着 Sui 在今年五月主网上线&#xff0c;它为 Move 生态带来一股新的浪潮。上线以来&#xff0c;Sui 公链的开发活跃度持续数月位居…...

RabbitMQ常用命令(一)

启动和关闭 1、启动RabbitMQ rabbitmq-server start & 注意&#xff1a;这里可能会出现错误&#xff0c;错误原因是/var/lib/rabbitmq/.erlang.cookie文件权限不够。 解决方案对这个文件授权 chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie chmod 400 /va…...

在教育领域,AI垂直大模型应用场景总结!

1. 智能教育助手&#xff1a; 这种模型可以通过语音或文本与学生进行交互&#xff0c;提供个性化的学习建议和答疑解惑。根据学生的学习习惯和知识水平&#xff0c;推荐适合的学习资源&#xff0c;并提供实时的辅导和反馈。 2. 智能作文批改助手&#xff1a; 这种模型可以对…...

基于级联广义积分器(CGI)的谐波信号提取MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 此方法可用于信号检测、虚拟阻抗合成、锁相环等方面。 在现有的信号提取方法中&#xff0c;众多学者采用了SOGI法、LPF法以及正交信号发生器等方法。当输入信号中不存在直流分量&#xff0c;只有谐波分量时&…...

Linux--线程-条件控制实现线程的同步

1.条件变量 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时&#xff0c;允许线程以无竞争的方式等待特定的条件发生。 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量&#xff0c;其他线程在获…...

flutter开发报错The instance member ‘widget‘ can‘t be accessed in an initializer

文章目录 问题描述问题原因解决方法 问题描述 The instance member ‘widget’ can’t be accessed in an initializer. 问题原因 “The instance member ‘widget’ can’t be accessed in an initializer” 错误是因为在初始化器列表中&#xff08;constructor initializer…...

spring项目详细结构目录

这里写目录标题 一、项目结构1、model模型类Student2、mapper 数据访问层接口和映射文件接口类StudentMapper接下来创建名为 StudentMapper.xml 的映射文件 3、service 服务层接口和实现类创建名为 StudentService 的 Java 接口创建名为 StudentServiceImpl 的实现类 4、contro…...

Cygwin 和MinGW 的区别与联系

Cygwin&#xff0c;是Cygnus出品&#xff0c;被Red Hat公司收购&#xff0c;名下的项目&#xff0c;目的为提供运行于windows平台的类Unix环境&#xff08;以GNU工具为代表&#xff09;。Cygwin提供了抽象层dll&#xff0c;用于将部分POSIX转换为Windows API调用&#xff0c;目…...

WebSocket Day03 : SpringMVC整合WebSocket

前言 在现代Web应用程序中&#xff0c;实时性和即时通信变得越来越重要。传统的HTTP请求-响应模式无法满足实时数据传输和双向通信的需求。随着技术的发展&#xff0c;WebSocket成为了一种强大而灵活的解决方案。 WebSocket是HTML5提供的一种新的通信协议&#xff0c;它通过一…...

Electron + VUE3 桌面应用,主进程和渲染进程通信

之前写过篇主进程和渲染进程之间的通信,这里主要是记录一下VUE版本的应用,主进程和渲染进程之间的通信。思路是一样,唯一不同的是代码。 在开发Electron应用的时候,从安全的角度来考虑,尽量不要在渲染进程中,直接调用ElectronAPI,因此我们需要预加载JS脚本,在这个脚本…...

使用腾讯云轻量服务器安装AList

新人有免费两个月试用轻量服务器&#xff0c;使用云服务器商自带的webshell登录&#xff1b; 我这儿用docker安装Alist&#xff0c;因为服务器没自带docker&#xff0c;所以具体安装docker centos7.0最快速安装docker的方法 通过 Docker 部署 Alist 命令&#xff1a; docke…...

边缘计算助力低速无人驾驶驶入多场景落地快车道

自动驾驶刮起的风&#xff0c;如今正吹向低速无人驾驶赛道。近期不完全统计显示&#xff0c;当前A股及港股正在排队IPO的自动驾驶相关企业共有12家&#xff0c;其中实现盈利的企业仅两家&#xff0c;而且实现盈利的两家企业最主要的收入并不完全源于自动驾驶领域。 相比之下&am…...

谷歌推出基于AI的产品图像生成工具;[微软免费课程:12堂课入门生成式AI

&#x1f989; AI新闻 &#x1f680; 谷歌推出基于AI的产品图像生成工具&#xff0c;帮助商家提升广告创意能力 摘要&#xff1a;谷歌推出了一套基于AI的产品图像生成工具&#xff0c;使商家能够利用该工具免费创建新的产品图像。该工具可以帮助商家进行简单任务&#xff08;…...

python学习10

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…...

【JAVA学习笔记】59 - JUnit框架使用、本章作业

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter15/src/com/yinhai/homework JUnit测试框架 1.基本介绍 1. JUnit是一个Java语言的单元测试框架 2.多数Java的开发环境都已经集成了JUnit作为单元测试的工具 2.如何使用 创建方法后&#x…...

3D 线激光相机的激光条纹中心提取方法

论文地址:Excellent-Paper-For-Daily-Reading/application/centerline at main 类别:应用——中心线 时间:2023/11/06 摘要 线激光条纹中心提取是实现线激光相机三维扫描的关键,根据激光三角测量法研制了线激光相机,基于传统 Steger 法对其进行优化并提出一种适用于提…...