聊聊测试驱动开发

news2024/11/28 13:54:49

这是鼎叔的第六十四篇原创文章。行业大牛和刚毕业的小白,都可以进来聊聊。

欢迎关注本专栏和微信公众号《敏捷测试转型》,星标收藏,大量原创思考文章陆续推出。

本文观点参考自Lasse Koskela,他是《测试驱动开发的艺术》的作者。

软件缺陷通常是由低质量的代码引起的,但是在复杂项目中,要维护这些代码简直就是噩梦。新加入的开发者想对它进一步修改,更是举步维艰。测试驱动开发,或许能解决这个问题,利用测试构建出高维护性和满足客户需求的软件,它也是XP(极限编程)的核心实践。

我们常说的TDD,通常指细节层面的UTDD(单元测试驱动开发),以测试驱动的方式编写开发。行业还有一个概念- ATDD(验收测试驱动开发),指在较高层次(特性功能层),以测试驱动的方式构建系统。前者保证内部质量,后者保证可见的外部质量。

TDD带来高质量和高效率

提倡短周期的TDD从一开始就能保证较高的代码质量,它完全颠倒了软件开发的旧方式(即先设计,然后编码实现,再测试),即先写测试描述目标,然后写代码达成目标,最后重构改进设计

完全可测试的代码和不断演化的简洁设计,能避免开发陷入代码越写越糟的恶性循环。TDD保证了所有代码都是可用,且被测试覆盖的。由于不用实现考虑实现细节,我们可以从更健壮的角度思考函数接口行为和外部调用方式。

写代码要基于测试先行的小步前进,增量式构建系统,在发生错误时很容易定位代码行,这样可以大幅节约后期的调试时间。我们也就有时间做更有意义的事,如清理代码,学习工具及技术。

而ATDD则是把客户需求全部转换成可执行的具体功能测试,拉近了客户和开发者的距离。在ATDD中我们更关注系统行为的测试,而非对象行为的测试。

TDD和ATDD非常互补,ATTD能驱动开发过程(做正确的事),而每个功能点上则使用TDD(正确地做事)。它们的组合让我们对交付的代码更有信心。

TDD三步曲(测试-编码-重构):正确地做事

三步曲代表着红(测试失败)-绿(测试通过)-重构代码的状态迁移。

写恰好足够的代码,仅仅是为了修复当前失败了的测试,不要一下子写出整个功能的代码再来花很长时间让测试通过。

所谓增量式开发(小步快跑),就是在“实现新功能”和“调整设计”两件事中来回切换,采用更经济的演进式设计方法。我们需要为可能的变化进行准备,但又要避免做无用功。最后的重构则是“在不改变外在软件行为的基础上,改进程序的内部结构”。

TDD整个过程要遵守严格的纪律,每次修改后运行自动化回归测试,用“测试PASS的信心”来交换开发速度。

ATDD-做正确的事

增量式开发模式中,客户有权决定哪些功能优先开发,从而被激发对项目的热情,而验收测试(AT- Acceptance Testing)则是整个团队(开发,测试,产品,客户)沟通的共同语言。“以需求文档为沟通媒介”很难清晰地表达出意图,而“以测试为规约”则更加精准、可靠和直接,缩短反馈周期。

测试驱动开发的工具支持

针对单元测试级别的TDD工具统称为xUnit。而ATDD的测试框架类型就更多了。前些年软件公司使用的最热门的验收测试工具是Fit/Fitness,表格形式的工具可以让非技术背景的客户和产品经理一起参与测试。Fit表格关联了测试夹具,可以自动执行表格内容的测试,并显示对用户友好的、多彩的测试结果。

此外还有纯文本的测试工具,比如通过关键字驱动或者利用日志来测试。

持续集成基础设施也是至关重要的保障,因为采用TDD的团队会共享代码,任何人都可以修改代码。

静态代码分析工具和代码覆盖率分析工具,对刚采用TDD的团队可以具体指出代码测试的不足,这个帮助很有必要。

TDD实践步骤详解

第一步:从需求到测试

开发者首先要把需求划分为要做的事(任务),用测试的形式来表达任务有利于我们记住要“完成”的定义,避免脱离了用户需求。一个好的“测试”应该是原子化的,独立的。

我们如何为一段还不存在的代码写测试呢?这需要我们想象产品代码应该如何易用,这种想象可以称之为“意图编程”。我们把注意力集中在“能有”的,而不是“已经有”的东西上,把需求分解为一系列小的,紧凑的测试。

第二步:用TDD开发模版引擎

第一步得到的测试列表是一个活文档,可以根据我们的进展而不断添加,接下来要让它们挨个通过。用自己认为合理的方式设计模板引擎的工作方式,把模板文本作为参数传给构造函数,验证结果与期望是否一致。

当然,编译器一开始会报错,因为某些类根本不存在,我们添加类,继续补充相应的方法。然后运行测试,测试必然会失败(因为我们还没有实现这些方法),继续补充最基本的产品代码,直到第一个测试通过,这个过程要尽可能简单快捷。

对于复杂逻辑的实现,我们可以采用广度优先或者深度优先的方式。如果是广度优先,我们会集中实现高层的功能,低层功能暂时用伪实现。若采用深度优先,我们会先实现底层功能,在所有底层功能都实现后才会组合来实现高层功能。

第三步,清除伪实现(尤其是硬编码),重构代码。

验证添加的测试确实被执行了,测试列表都通过了(包括特殊情况的数据测试),我们通过重构避免代码的“腐坏”,清理重复和冗余的代码,移除多余的测试。利用夹具使测试更加紧凑,用测试替身替代真实对象。

第四步,添加错误处理,最后让代码尽量精简。注意验证异常中的详细信息,保持方法中的代码抽象层次的一致性,这样会提高代码的可读性。

第五步,增加更多的系统测试,如耗时的性能测试。测试替身可以提高测试速度,降低依赖性。

TDD的指导原则

总之就是:绝不跳过重构,尽快变绿,犯错后减慢速度。

为了提高可测试性,设计上要尽量使用组合而非继承,掌握参数化测试手段(数据驱动测试),正确恰当地隔离依赖。

如果我们是在糟糕的遗留代码上进行TDD,首先要进行代码分析,确定变更点代码,进而确定测试点,从近距离测试,也从远距离寻找合理的测试点(如网络和日志),小心地移除某些依赖,并暴露依赖的接缝。一旦有了足够的测试覆盖,就可以放心地引入变更。

集成测试中的TDD

TDD并非只对应单元测试,集成测试也是用来做TDD的。和单元测试的不同在于,集成测试会真实地访问数据库,访问文件系统,花费的时间更多,需要更完整的基础设施,可能需要改变数据库模式,进行针对性的重构。不足之处是集成测试难以模拟特定的异常场景。建议我们充分利用单元测试和集成测试两者的优点来实践TDD。

多线程并发是TDD实践的难点,代码中的任何同步都会对相邻线程的并发性产生影响,因此并行编程出错的可能性更大,还可能遇到“死锁”和“饥饿”等现象。我们需要针对“线程安全”进行编码,在测试中尽量避免用“钩子”来控制产品代码的执行过程,以及等待验证异步调用的结果。

ATDD的进一步阐述

验收测试是用业务问题领域的语言来描述的测试用例,描述简洁准确,无歧义,侧重于“做什么”和原因,而非“如何做”。最终的所有权属于客户(利益相关方)。测试角色在团队中既属于领域专家,也属于技术专家。

验收测试的格式,可能是声明式的表格结构,因此验收测试自动化工具可能和系统实现的语言不同,强调客户容易理解,简单易懂。ATDD要在功能层面保证“软件做了我想要的事情”,而不是从技术上保证。

一个ATDD过程周期非常简单,分为:挑选一个用户故事(从需求中拆解)、写测试用例、自动化测试、实现功能,这四大步骤。

建议一步步实现验收测试,而不是一下都实现。大部分团队都会自己实现验收测试,不依赖于专门的验收测试人员。

注意,实现功能这个“第四步”,就可以扩展为一个或一系列的TDD小周期(测试-编码-重构),这样ATDD和TDD就在不同层次上形成紧密协作的闭环,相辅相成。

ATDD给出了需求完成的“定义”,通过有意义的例子,而不是复杂模糊的描述来表达需求。每个人都会贡献自己特有的知识和技能来解决问题。客户能看到自己的需求被真正满足了;开发人员看到客户参与验收测试,并认可了自己代码的价值。

验收测试是否应该操作真实用户的外部界面?答案是“看情况”,如果真实界面难以访问,或者其反馈成本高、性能慢,我们也可以绕过界面,通过API或者内存数据库来进行验收测试。但是在这么做之前,我们先要确认替换后的被测系统和替换之前是否足够相近,或者不得不这么做。

验收测试不必测试所有东西,而是聚焦用户故事的本质特质,同时避免波动频繁带来的维护性问题,选择技术障碍最小的地方越过它。

实现ATDD的方式主要有:

1 端到端。理想情况下,应该把被测系统当成一个整体,端到端的视角,和客户观察系统的角度一样,最能体现系统的真实情况,以及对系统的广泛覆盖。但是这种测试太脆弱,尤其UI变更频繁,同时速度也慢。

2 绕过UI的测试,即绕过系统的壳,避免了不必要的改动,调用抽象UI或者API来访问系统内部。这样易于实现且执行速度快,但会让客户困惑,也需要手工测试弥补图形界面的测试质量。

3 直接测试内部逻辑。利用验收测试工具,把业务逻辑隔离再几个精准的测试中。它和单元测试的不同在于,前者是用客户领域的语言编写的,后者是为开发者编写的。

写在最后,在ATDD技术实践上可以用到的技巧还包括:

1 把系统的一些非关键构件替换成测试桩或者仿真器,利用测试后门(替代性接口)等。

2 加快测试执行速度。比如检查所有的测试是否真的需要这么多的初始化,以及用一次初始化完成一批用例的前置准备。

比如把测试套件分为两组,一组是有副作用的(如写数据库),一组是没有副作用的,先执行后者。

还有,减少磁盘I/O访问的动作,或者分布式执行任务,利用好负载均衡。

3 减少测试的复杂度。如消除代码重复,优化命名,利用公共函数把验收测试组织成有机的整体,利用缓存环境对象。

4 管理好测试数据。在保持好自动化验收测试的代码干净整洁的同时,提高测试数据的可管理性。如把测试数据小块化,或动态产生测试数据,以及对测试数据做好版本控制。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试之【单元测试、系统测试、集成测试】

一、单元测试的概念 单元测试(Unit Testing)是对软件基本组成单元进行的测试,如函数(function或procedure)或一个类的方法(method)。当然这里的基本单元不仅仅指的是一个函数或者方法&#xff0…

揭秘跨部门沟通的秘密武器:让不归你管的人主动配合你的绝妙方法!

跨部门沟通,Edge对此有点胆怯:“我们自己内部进度,怎么着都好管。都是自己人,目标一致。可涉及跨部门合作,管起来就困难。人家又不归我们管,不可控因素太多了。如果在合作的过程中,出现啥问题&a…

docker 镜像结构原理

目录 参考文档:第八篇:Docker镜像结构原理_Linux运维开发的技术博客_51CTO博客 1、基础镜像 base base 镜像有两层含义: 为什么我们的镜像文件比一般的软件小一些呢? 二、镜像的分层结构 问什么 Docker 镜像要采用这种分层结…

org.yaml.snakeyaml.parser.ParserException: while parsing a block mapping

一、yml解析异常问题 今天启动某开源项目时,碰到一个问题org.yaml.snakeyaml.parser.ParserException: while parsing a block mapping 。 二、解决 2.1 修改项目目录下缩进格式 在项目下的nacos目录中application-common.yml 文件中修改缩进格式,每…

15个最好的性能测试工具(软件测试工程师必备)

在软件测试日常工作中,大家接触得比较多的性能测试工具有LoadRunner和Jmeter,这里整理了web应用程序性能和负载压力能力的最广泛使用的性能测试工具的综合列表。 这些负载测试工具将确保您的应用程序在高峰流量和极端压力条件下的性能。 该列表包括开源…

Spring Security OAuth2.0(五)-----OAuth2实现自定义统一认证登录页/自定义授权页/基于mysql存储数据

本次实例涉及三个项目 核心项目工程unify_authorization_server(认证授权登录) 资源服务器项目unify_resource_server 测试项目是前面几篇写的项目 这里没有改动直接用来测试实例项目 (一)unify_authorization_server pom相关依赖 我采用的是spring-bo…

Ngnix网站服务

Ngnix网站服务 Ngnix网站服务 一、Ngnix服务基础:1.Ngnix的基本概述:2.简述Nginx和Apache的差异:3.Nginx和Apache的优点:4.应用场景:5.扩展: 二、编译安装Ngnix服务:1.编译安装:2.常…

基于matlab使用自校准来适应阵列不确定性(附源码)

一、前言 此示例显示了基于约束优化过程的自校准过程。利用机会来源同时估计阵列形状的不确定性和来源方向。此示例需要优化工具箱。 理论上,可以设计一个完美的均匀线性阵列(ULA)来执行各种处理,例如波束成形或到达方向估计。通常…

玩转ChatGPT:回答审稿人问题

一、写在前面 前段时间一篇时间序列预测的文章返修,还挺幸运的,给了个小修。 不过问题也问得有点刁钻,应该是个行家。 想到手头有小Chat,打算使用TA来辅助我回答审稿人问题。 以下展示仅仅提供一个工作流和思路,具体…

好程序员:逼自己看完并学会,你的Java会很牛!

打算学java的伙伴们,如果你们很迷茫焦虑的话,不妨看看好程序员的建议。好程序员作为行内人告诉大家,零基础也是可以学java的,而且不仅可以学会,还可以学的很好,并且能找到工作。 Java学习路线规划&#xff…

Day22 实战篇 ——Jmeter性能测试实战——JMeter执行原理、Jmeter性能测试实战、下载使用PerfMon插件、 Grafana可视化展示

Day22 实战篇 ——Jmeter性能测试实战——JMeter执行原理、Jmeter性能测试实战、下载使用PerfMon插件、 Grafana可视化展示 文章目录 Day22 实战篇 ——Jmeter性能测试实战——JMeter执行原理、Jmeter性能测试实战、下载使用PerfMon插件、 Grafana可视化展示一、Jmeter执行原理…

高性能通信库——nanomsg(含交叉编译)

一、nanomsg介绍 NanoMsg是一个Socket的通讯库,使用C语言编写实现的,这样就可以适用于多种操作系统,而且几乎不需要什么依赖,可扩展并且能易于使用。Nanomsg提供了几种常见的通信模式 ( 也称为“可扩展性协议” &#…

Flutter 小技巧之 InkWell Ink 你了解多少

今天要介绍一个「陈年」小技巧,主要是关于 InkWell 的基础科普,InkWell 控件相信大家不会陌生, 作为 Flutter 开发中最常用的点击 Widget ,配合 Flutter 自带的 Material ,可以轻松实现带有水波纹等的点击效果。 而之所…

VSCode 安装配置教程详解包含c++环境配置方法

vscode安装教程及c环境配置详解 vscode下载安装下载C扩展插件VScode C环境配置配置环境变量检查 MinGW 安装配置编译器:配置构建任务检查是否安装了编译器配置完毕 vscode下载安装 地址:官网下载地址 直接打开下载好的.exe文件进行安装即可&#xff0…

如何使用 PowerPoint 2021 制作演示文稿?

软件安装:办公神器office2021安装教程,让你快速上手_正经人_____的博客-CSDN博客 引言 PowerPoint 是一款非常常用的演示文稿制作工具,它可以帮助您创建漂亮的幻灯片,展示您的想法和信息。如果您是 PowerPoint 的新手&#xff…

xx客滑块

xx客滑块 网址流程1、访问首页,得到网页源代码得到 sessionId2、生成dInfo参数(getInfoTp接口使用到),是AES 加密(不校验)3、访问 /captcha/getInfoTp 得到responseId (可以认为是图片id&#x…

上海细化“元宇宙”概念 落地场景仍待破局

日前,一份关于“元宇宙”更加具体的行动方案引发业内高度关注:上海发布《上海市“元宇宙”关键技术攻关行动方案(2023—2025年)》的通知。 这说明政府层面开始进一步细化,以更好推动‘元宇宙’产业的发展。” 主攻沉…

【强烈推荐】基于STM32的TFT-LCD各种显示实现(内容详尽含代码)

前言:TFT-LCD模块作为人们日常生活中常见屏幕类型之一,使用的受众面非常广阔。例如:显示各个传感器数值,显示精美界面,多级化菜单系统等等都不离不开他的身影。可以说学会TFT-LCD模块是嵌入式开发必须掌握的驱动开发技…

AOP切面记录日志

AOP切面记录日志 一、导包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>二、写一个注解 /*** 用于切面记录日志用的注解&#xff0c;只能加在方法中使用* a…

让性能腾飞!亚马逊云科技的 Java 云端之旅

在上篇文章中&#xff0c;我们为大家介绍了亚马逊的 Java 生态及丰富的开发工具、框架。本文将分享亚马逊的 Java 架构、迁移途径&#xff0c;并分享一个具体实例&#xff0c;介绍如何使用机器学习来构建 Java 应用和提升 Java 性能。 亚马逊云科技开发者社区为开发者们提供全…