写点什么

编写良好的单元测试

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

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

关注

评论

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

PCB如何设计防静电?华秋一文告诉你

华秋电子

简单学习一下 MyBatis 动态SQL使用及原理

做梦都在改BUG

Java mybatis

从 MySQL 到 Oracle 再到全面 TiDB ,云盛海宏的数据库架构实践

TiDB 社区干货传送门

如何使用appuploader制作apple证书​

雪奈椰子

2023年报业网络安全等级保护定级流程

行云管家

网络安全 等级保护 报业

不服不行!Github爆火的「高并发秒杀顶级教程」,先睹为快

做梦都在改BUG

Java 高并发 秒杀系统

跑步课程导入能力,助力科学训练

HarmonyOS SDK

HMS Core

Elasticsearch分布式搜索引擎的基本使用

北桥苏

php elasticsearch

国内商业BI工具介绍,瓴羊Quick BI、帆软怎么样

流量猫猫头

Windows本地搭建RabbitMQ Server

北桥苏

Rabbit MQ RabbitMQ安装

不到1分钟,帮你剪完旅行vlog,火山引擎全新 AI「神器」真的这么绝?

字节跳动技术范儿

字节跳动 算法 计算机视觉 云服务 火山引擎

并发编程-ReentrantLook底层设计

做梦都在改BUG

Java 并发编程 ReentrantLook

OSS云文件列举分页

北桥苏

php OSS 分页 上传 thinkphp

中建信息亮相华为中国合作伙伴大会2023

Geek_2d6073

方案精讲丨TiDB 在社交场景的解决方案实践

TiDB 社区干货传送门

这套Github上40K+star面试笔记,可以帮你搞定95%以上的Java面试

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

从零开始打造一款基于SpringBoot+SpringCloud的后台权限管理系统

做梦都在改BUG

Java Spring Cloud Spring Boot 权限管理

AntDB数据库携手金蝶Apusic应用服务器, 共促信创产业繁荣发展

亚信AntDB数据库

AntDB AntDB数据库 企业号 5 月 PK 榜

开源轻量级 IM 框架 MobileIMSDK 的Uniapp客户端库已发布

JackJiang

网络编程 即时通讯 即时通讯IM

重磅来袭!微服务的里程碑SpringCloudAlibaba

做梦都在改BUG

Java 架构 微服务 Spring Cloud spring cloud alibaba

ZeroErr 零误框架

西风逍遥游

膜拜!阿里内部都在强推的K8S(kubernetes)学习指南,不能再详细了

做梦都在改BUG

Java Kubernetes k8s

文盘Rust -- rust连接oss

TiDB 社区干货传送门

开发语言

PHP快速使用RabbitMQ实现项目中部分业务的解耦

北桥苏

php RabbitMQ thinkphp

Java面试题大全(整理版)1000+面试题附答案详解最全面看完稳了

Java你猿哥

Java MySQL redis mybatis java面试

【源码分析】【seata】at 模式分布式事务-server端与客户端交互

如果晴天

源码分析 分布式事务 分布式锁 seata Seata框架

2023 年最新 Java 面试必背八股文,1338 道最新大厂架构面试题

架构师之道

Java 面试

耗时一个月,整理了这份大厂Java面试 / 学习指南,共计1500+ 题全面解析

采菊东篱下

Java 程序员

小心白蛇!PyPI仓库被持续投放White Snake后门组件

墨菲安全

pypi 开源软件供应链安全

短信验证 创建应用

MobTech袤博科技

Confluence(知识库软件)和腾讯文档(多人协作文档软件)的区别

爱吃小舅的鱼

Confluence PingCode 文档管理工具

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