写点什么

编写良好的单元测试

  • 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:006899
用户头像

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

关注

评论

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

W3C白皮书之小程序技术标准

FinFish

小程序 W3C 小程序化 miniapps

mysql与redis的选型问题实践

苏格拉格拉

MySQL 数据库 持久化 redis'

挖掘数据价值,华为云大数据BI解决方案有绝招

科技怪授

做电商的看过来,华为云大数据BI方案驱动业务增长

科技怪授

富士康智能制造加速营:解决行业当下问题,创造核心技术价值

B Impact

性能测试知识科普(七):监控能给你带来什么

老张

性能测试 监控指标

华为云大数据平台,助力企业数字化转型成效明显

爱尚科技

华为云大数据轻量级解决方案,让数据“慧”说话

科技怪授

Linux vi/vim

芯动大师

vim教程 11月月更 Linux指令

支持向量机-二分类SVC中的样本不均衡问题:重要参数class_weight

烧灯续昼2002

Python 机器学习 算法 sklearn 11月月更

一文熟悉 Go 函数

陈明勇

Go golang 闭包 函数 11月月更

【web 开发基础】PHP使用自定义函数库 (39)

迷彩

web开发基础 11月月更 自定义函数 函数库引入 函数库

云视频会议哪家强?华为云会议更专业“会”更好!

i生活i科技

【SpringBoot】配置文件的加载与属性值的绑定

石臻臻的杂货铺

springboot 11月月更

华为云大数据BI 解决方案的超高性价比

IT科技苏辞

华为云大数据,帮助企业搭好数据底座

IT科技苏辞

2022-11-28:给定两个数组A和B,比如 A = { 0, 1, 1 } B = { 1, 2, 3 } A[0] = 0, B[0] = 1,表示0到1有双向道路 A[1] = 1, B[1]

福大大架构师每日一题

算法 rust 福大大

以原子化服务为基石,面向三大场景打造鸿蒙软硬协同新体验

最新动态

龙蜥技术委员会主席杨勇:下一代操作系统展望

云布道师

开源 操作系统

论文领读|基于 VQVAE 的长文本生成

澜舟孟子开源社区

人工智能

python任务调度利器-APScheduler

AIWeker

Python 任务调度 11月月更

三分钟了解client-go

穿过生命散发芬芳

Client-go 11月月更

项目管理软件有哪些?

优秀

项目管理软件

适用场景全新升级!扩展 Dragonfly2 作为分布式缓存系统架构 | 龙蜥技术

OpenAnolis小助手

开源 架构 分布式 龙蜥技术 dragonfly2

亚马逊云科技云从业者加速训练营上线啦!

亚马逊云科技 (Amazon Web Services)

培训与认证

Kotlin类声明和构造器(constructor)

子不语Any

android kotlin 11月月更

Java通讯录管理系统

我是一个茶壶

Java 11月月更 通讯录管理系统

Flare Network,跨越互操作性三难困境

股市老人

从传统数据治理转向主动数据治理!

用友BIP

华为云大数据BI解决方案助力“智能智造”

爱尚科技

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