单元测试之如何编写优秀的单元测试用例

这篇文章是结合"The Art of Unit Testing"一书及业内一些单元测试大牛们的总结整理而成。主要内容是实际工作中如何运用一些技巧和方法编写出可靠、可读、可维护的优秀单元测试用例。

优秀单元测试的定义

单元测试:一段** 自动化** 的代码 ,这段代码调用被测试的** 工作单元 ,之后对这个工作单元的 单个最终结果 的某些假设进行检验。单元测试几乎都是用 单元测试框架进行编写。单元测试容易编写快速运行可自动化可靠可读可维护结果稳定**。

集成测试:对一个工作单元进行的测试,这个测试对被测试的工作单元没有完全的控制,并使用该单元的一个或多个真实依赖物,例如数据库、系统时间、系统文件等

工作单元:从调用系统一个公共方法到产生一个测试可见的最终结果,其间这个系统发生的行为。一个工作单元既可以是小到只包含一个方法,也可以大到包含实现某功能的多个类或方法。

最终结果:是指被调用的公共方法返回一个值;或者在方法调用前后,系统的状态或行为有可见的变化,这种变化不需要查询私有状态即可判断;又或者是** 调用了一个不受测试控制的第三方系统 **,这个第三方系统不返回任何值,或者返回值已被忽略。

编写可靠的测试

所谓可靠性是指单元测试本身是正确的,即该失败的时候失败,该成功的时候成功。只有保证单元测试的可靠性,才能让开发人员信任该单元测试,不会为了以防万一进行调试等其他工作。
在"单元测试的艺术"中,作者给出了一些简单的原则和技术帮助编写可靠的测试。

  • 依据实际情况合理地删除或修改单元测试
    如果确定是测试缺陷,而不是产品缺陷(被测试代码缺陷)时,需要立刻修改相关单元测试代码;如果被测试的产品代码的语义或者API变更导致测试失败,这时是需要修改测试,使用新的语义;如果看到测试名含义不清或者单元测试的可维护性差就应该在保证单元测试基本功能前提下修改测试名称或者重构测试;如果同一个功能多个单元测试,请删除重复测试。
  • 避免在单元测试代码中包含逻辑
    包含逻辑的测试是指测试代码中包含switch、if/else、for/while等控制流语句。这样的测试可读性差,代码脆弱,测试代码的复杂度高,容易包含缺陷,测试结果不容易重现。
  • 每个单元测试只测试一个关注点
    所谓的一个关注点就是指一个工作单元的一个最终结果:一个返回值、系统状态的一个改变、对第三方对象的一个调用。测试多个关注点一方面不利于测试命名,另一方面很多单元测试框架中,一个失败断言就会抛出一个特殊类型的异常,后面代码不会继续执行,这样不利于收集测试失败原因。
  • 区分单元测试和集成测试
  • 用代码审查确保代码覆盖率
    如果你做了代码审查、测试审查、确保测试优秀而且覆盖了所有代码,那么就可以避免犯简单愚蠢的错误,同时也可以从持续的学习中获益。

编写可读的测试

单元测试可以看做是一个婉婉道来的故事,这个故事是讲给下一代开发者听,故事的内容就是这个应用程序的组成及其流程。既然是故事,就一定要形象生动,易于理解。那么可读性就是指如何确保其他开发者能够理解他们要做的工作,以便维护产品代码和测试。
单元测试的可读性其实和代码可读性在很多方面类似,下面就逐一讨论这些方面。

  • 单元测试的命名标准
    合理地命名测试,主要目的是为了使后来的开发者从为了理解测试而阅读代码的负担中解脱出来。测试名应该包含三部分:被测试方法名、测试场景(即测试使用的条件)、预期行为(即被测试方法的最终结果)。
  • 单元测试中的变量命名规范
    单元测试除了主要的测试功能之外,它还为API提供某种形式的文档。通过合理命名变量,帮助阅读测试的人可以尽快理解你要验证什么(从而更加理解产品代码中想要实现什么功能)。
  • 断言和操作分离
  • 避免滥用setup和teardown
    比如在setup中准备stub和mock对象,这种情况就会导致阅读测试的人意识不到测试中使用了模拟对象,也不知道这些模拟对象预期是什么。

编写可维护的测试

可维护性是大多数单元测试面临的最大挑战。随着时间累计,单元测试似乎越来越难维护和理解,被测代码一个微小的改动,似乎都会使某个测试失败。那么怎么能够尽量降低可维护性的成本呢?

  • 只测试公共契约,避免测试私有或者受保护的方法
    私有方法可以看做是系统内部契约,这个内部契约是动态,在系统重构时可能会被随时修改,因此针对这些内部契约的单元测试也很可能会失败。而内部契约最终都会被一个公共契约(公共方法、整体功能)所调用,也就是说任何私有方法通常都是一个更大的工作单元的一部分。
  • 去除重复代码
    可以使用辅助方法或者setup来去除重复代码的问题
  • 实施测试隔离
    测试隔离是指每个测试都只生活在自己的小世界中,它与其他测试之间没有任何依赖关系,甚至不知道其他测试存在。
    下面列举几种常见的测试隔离的反模式。
    1、测试结果依赖测试执行的顺序
    2、测试调用其他测试方法
    3、测试中使用的共享资源(内存或外部资源)没有得到清理或回滚
  • 避免对不同关注点多次断言,尽量使用参数化测试或者对每个关注点设计单独的测试用例
  • 避免过度指定
    常见的过度指定的例子。
    1.对系统内部契约进行断言
    2.使用过多的模拟对象
    3.精确匹配
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 0x00 单元测试Pro & Con 最近尝试在我参与的游戏项目中引入TDD(测试驱动开发)的开发模式,因此单元测...
    陈嘉栋阅读 803评论 0 3
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 在你不知道的时间里爱着你。
    Mooben阅读 142评论 0 0
  • 现在让我们来做一个打开自己的练习,把那些我们想要带进我们生命的能量先给出去。我把它叫做“莲花冥想”,这是一个非常有...
    道心禅阅读 3,102评论 0 3