写点什么

编写良好的单元测试

  • 2017-02-03
  • 本文字数:3331 字

    阅读完需:约 11 分钟

尽量保持较小的单元测试规模,使用恰当的工具,将程序员和测试人员配对;这是编写良好的单元测试的一些建议。单元测试混合了编程和测试;程序员和测试人员要一起工作,互相学习,拓展自己的知识面。

Adrian Bolboacă是 Mozaic Works 的组织和技术教练 & 培训师。在 2017 欧洲测试大会上,他介绍了不同类型的自动化测试。InfoQ 将以 Q&A、综述和文章的形式对此次大会进行追踪报道:

[欧洲测试大会] 是让专家和实践者聚在一起,交流、学习和实践软件测试艺术。我们正在研究先进的新方法,以便让我们的测试更有效,加深对基本方法的理解,培养更强大的社区。

在博文“自动化测试目的”中,Bolboacă指出,单元测试应该完成如下工作:

单元测试关注一个方法或一个类。它应该非常小,最多只有几行代码。人们在编写单元测试时会犯许多错误,所以这不是小事。因为它们非常小,所以它们应该在内存中运行,而且,一个单元测试应该在几毫秒内运行完成。任何用到外部依赖(数据库、WebService、文件系统、I/O)的测试都不是一个单元测试,那是其他的东西(“集成测试(integration test)”、“综合测试(integrated test)”、验收测试、端到端测试,等等)。

InfoQ 就编写良好的单元测试及在单元测试中利用自动化采访了 Bolboacă。

InfoQ:谁编写单元测试重要吗?是开发人员,还是测试人员?

Adrian Bolboacă:单元测试更偏技术,它们通常关注代码细节,甚或是编程语言特有的概念。在类似 Java 这样的静态编程语言和类似 Ruby 这样的动态编程语言中,单元测试看上去是不一样的。这就是为什么编写单元测试主要应该由程序员负责。

另一方面,测试人员更了解如何制定测试计划,根据特定分析,如等价类划分和边界值分析,定义有价值的值。因此,程序员需要从测试人员那里“窃取”这类知识,或者,他们可以和测试人员结对,一起讨论需要编写的测试,但是,之后应该由程序员实现它们。

根据我的经验,结对是最好的选择,因为测试人员和程序员可以更好地互相学习,拓展自己的知识面。

InfoQ:什么时候应该编写单元测试?

Bolboacă:团队可以在生产代码编写完成之后编写单元测试。我们称之为“测试延后(test after)”。但是那通常很困难,因为在编写生产代码时需要时刻考虑可测试性。如果我们选择了这种方法,则生产代码需要经过一个代码审核流程,以确保它是可测试的。只有在完成这项工作之后,程序员和测试人员才可以继续结对创建测试。

也有一种方法是“测试优先(test-first)”,我建议从同伴那里“窃取”了大量测试知识的程序员使用这种方法。使用这种方法时,我们先分析问题,然后编写一个单元测试,最简单的实现代码,一个单元测试,实现它,依此类推。当团队达到了这个水平,我会说,编写单元测试的程序员是半个测试人员了,因为“测试优先”方法需要大量的测试知识。在这种情况下,测试人员最终将专注于审核单元测试及编写验收测试。

在采访“测试优先方法”中,Gil Zilberfeld 谈了测试优先方法所带来的好处:

测试优先方法定义了需要做什么。它定义了我们为解决特定问题而需要编写的代码,因为我们有一个测试形式的定义。只要运行测试,我们就很容易知道我们的功能是否有效。

采用这种方法可以获得更高的覆盖率,因为测试成了一等开发活动,而不是拖到最后。

此外,在编写这些测试并详细说明场景时,我们更深入地了解了问题空间,因为许多问题会被提出来。在测试延后方法中,这些讨论有时候都不会发生,开发人员按照自己的想法编写代码,而不是根据解决方案的需要。

在文章“ QA 部门消亡日”中,Eli Lopian 解释了单元测试为什么可能成为 QA 杀手:

单元测试是一种测试特定代码片段的方法,它可以确保该代码段可以正常运行并且契合软件拼图。有证据表明,借助单元测试,你可以检查超过 90% 的代码,而且,和 QA 的手动测试工具不同,恰当构建、可以自动测试的单元测试可以随着代码库一起演化,实时测试代码。

InfoQ:您在单元测试中如何利用自动化?

Bolboacă:单元测试是一个分析过程,会产生一份测试计划,然后测试计划会被自动化。

为了实现这些测试的自动化,请记住以下几个要点:

  1. 使用领域语言命名测试名称
    通常,我们就只管编写测试。但是,读代码的次数要多于写代码。因此,要善待你的同事和未来的自己,清晰地命名测试。使用领域名称,而不是技术名称。类似“ExceptionOnOverflow”、“TestThree”和“CustomerTest”这样的名称并不清晰。取而代之,我们应该使用类似“WhenTooManyPlayersAreAddedAnErrorIsReturned”、“ACustomerNeedsAllFieldsValidated”、“ValidCustomerCanBeUsedByOrder”和“InvalidCustomerIsRejectedByOrder”这样的名称。这样,所有了解业务领域的人都会清楚你那里在测试什么,甚至是你的客户。
  2. 编写短测试
    单元测试要短而清晰,并且只有一个目的。最好的测试有 3 到 4 行代码,那样,对任何阅读测试代码的人而言都会非常清晰。我们需要有许多类似这样的小的单元测试,它们一起构成了产品的免疫系统,其中每个测试都像是一个免疫细胞。如果出现了 Bug,则会有一个小测试准确地告诉你问题在哪里。这样,你就会得到迅速的反馈和一个简单的开发测试周期。
  3. 每个测试一个断言
    如果你在一个测试中包含了不只一个断言,则你的测试目的就不只一个。在这种情况下,测试名称变得奇怪不清晰,测试变得太长,反馈也变得不清晰;你永远无法知道哪个断言通过了,哪个断言失败了。假如你依次有三个断言。如果第一个断言失败了,则后面两个永远都不会检查。如果你修改了一些生产代码,那么当代码变化时,后面两个断言就无法发挥作用了。在这种情况下,你就会错误地认为自己的代码有安全保障和回归测试。
  4. 不要链接测试
    我经常看到人们有链接测试的习惯。这通常是因为准备工作非常困难。但这不是一个解决方案。在测试链中,依赖于其他测试的测试之所以失败,大多数情况下都是因为前面的测试失败了。在这种情况下,你可能会修改代码让测试通过,但你可能会因为测试不完备而引入没有验证到的缺陷。测试应该总是相互独立,就像免疫系统里的免疫细胞那样。它们都依赖于产品,而不是互相依赖。
  5. 使用恰当的工具
    测试工具有许多:测试框架、模拟框架、测试执行器、性能测试工具、安全测试工具,等等。你要确保选择了适合工作任务的工具。不要仅仅只是使用你知道的工具。通常,xUnit 框架非常适合于大多数的自动化测试类型,但性能测试和安全测试要使用专门的工具。如果你希望让良好的测试成为可执行的规范,那么你也可以使用 xUnit,但是,你也可以使用一些 BDD 框架来简化测试工作。请记住,从中长期来看,选择一款测试框架是你必须作出的决策。要考虑学习成本以及使用和维护成本。

由于当前市场对特性的交付速度要求越来越高,我们需要用一种巧妙的方式自动化产品验证过程。这就是为什么自动化如今是必不可少的。

InfoQ:对于编写良好的单元测试,您有什么建议吗?

Bolboacă:单元测试源于工业生产,大多数人都忘记了这一点。在工业上,我们需要测试每一个小部件,看它的质量是否合格,我们是否可以把它用在下一步的部件装配中。在软件行业,我们没有这样清晰的单元定义;人们有不同的看法——方法、类、模块。第一个难点是让所有的团队成员对单元是什么有一个共识,并编写相应的测试。在我看来,单元非常小,就像发动机中的螺丝或者家具中的钉子那么大。所以,我建议定义单元是什么,并在测试审核阶段对此进行密切关注,确保其得到了执行。

单元测试混合了编程和测试。为了编写简单、快捷、可维护的测试,程序员需要知道许多测试概念。我对程序员的建议是从负责测试的同事那里学习一些基本的概念:等价划分、边界值分析、测试覆盖、正向测试、逆向测试。

关于单元测试,还有另外一个经常被遗忘的部分,那就是分析。我们不能不经过思考就立即开始编写测试。我们需要分析问题,如果可能对它进行划分,然后再考虑我们需要编写的单元测试。这里有一个不错的建议,就是总是从系统输出开始,然后找出生成那个输出的可能输入。感谢 Chris Matts 把这个技巧教给了我。

在编写单元测试时,我们应该使用来自问题领域的词汇。测试应该体现一项特性存在于产品中的理由。一个了解业务领域但不了解编程的人也应该能够阅读你的测试。在那方面,和分析师结对非常有用。

查看英文原文 Writing Good Unit Tests

2017-02-03 18:006747
用户头像

发布了 1008 篇内容, 共 402.0 次阅读, 收获喜欢 345 次。

关注

评论

发布
暂无评论
发现更多内容

位运算——Java语言描述

工程师日月

位运算 java 5月月更

LabVIEW串口通信

不脱发的程序猿

LabVIEW 串口通信 数据通信

[Day37]-[二叉树]- 找树左下角的值

方勇(gopher)

LeetCode 二叉树 数据结构算法

时序数据库在博物馆环境检测的应用

CnosDB

IoT 时序数据库 开源社区 CnosDB infra

DevOps系列之 —— 持续规划与设计(二)规划与设计

若尘

DevOps 5月月更

成功转行测试,分享一下自己的经验【思维导图】初级/中级/高级测试工程师会哪些...

伤心的辣条

Python 程序人生 软件测试 自动化测试 测试开发

Django 如何获取 Model 字段列表?

AlwaysBeta

django

跟着动画学 Go 数据结构之二叉树

宇宙之一粟

数据结构 二叉树 Go 语言 5月月更

Hexo+github搭建个人博客,并绑定域名

武师叔

5月月更

LabVIEW应用程序后台运行

不脱发的程序猿

LabVIEW

LabVIEW串口调试助手

不脱发的程序猿

LabVIEW 串口通信 数据通信 串口调试助手 VISA

恒源云(Gpushare)_今日炼丹小疑问:如何给数据加权重?

恒源云

Python 深度学习 PyTorch

恒源云 (Gpushare)_【炼丹必备】调参心法(说人话系列)

恒源云

深度学习

在线URL编码加密工具

入门小站

工具

nginx配置系列(九)nginx中的防盗链

乌龟哥哥

5月月更

做SaaS的程序员们,是时候关注企业架构了

AI架构师汤师爷

企业架构 SaaS 架构设计 5月月更

linux之autojump命令

入门小站

Linux

查找端口占用并关闭进程(windows)

liuzhen007

端口占用 5月月更

MathType全新免费版数学公式编辑器

茶色酒

MathType

CleanMyMac有没有需要安装电脑?

茶色酒

CleanMyMacX

HarmonyOS 2迎来大更新:10个功能升级,这些机型建议更新!

科技汇

测试人生 | 00后0经验应届毕业生拿下2线城市15W offer,好励志~

伤心的辣条

Python 程序人生 软件测试 自动化测试 接口测试

技术打开感知世界:当感官数字化,会发生什么?

脑极体

MathType2022永久无限试用脚本程序

茶色酒

MathType

LabVIEW十六进制和字符类型转换

不脱发的程序猿

LabVIEW 进制转换

PyTorch 开发环境搭建

Emperor_LawD

PyTorch 5月月更

2022 开源之夏 | Serverless Devs 陪你“变得更强”

阿里巴巴云原生

阿里云 云原生 Serverless Devs 开源之夏

从开源模型、框架到自研,声网 Web 端虚拟背景算法正式发布

声网

AI 大前端 WebRTC webassembly Dev for Dev

六、高可用之流控降级

穿过生命散发芬芳

5月月更 高可用设计

【愚公系列】2022年05月 二十三种设计模式(六)-适配器模式(Adapter Pattern)

愚公搬代码

5月月更

CentOS 8及以上版本配置IP的方法,你 get 了吗

伍工

Linux 网络

编写良好的单元测试_语言 & 开发_Ben Linders_InfoQ精选文章