Angular 单元测试策略
本文假设读者已经具备基本的单元测试意识、工具知识,不再赘述基本概念,如 jasmine 基本知识、断言语法、karma 配置等。
理论篇
为什么需要测试
需要测试应该说已经渐渐成为行业里软件开发的政治正确了。然而,对于为什么需要测试,测试能带来什么样的价值,大家不一定有清晰的感知。在 React #200 那篇文章里,我从 cycle time 这个角度去讲为什么要写测试,现在可以再拔高一个层次来讲:响应力。先不谈组织级别的响应力,单从 IT 响应力来讲,它意味着你能多快地响应变化:新增需求、需求变更、线上问题、bug 定位、验证价值…等。而 cycle time,其实就是响应力的度量标准。
它就像买保险,在你没有买保险的意识之前,总会有这么个疑问:万一我买了没事那不就亏大了么?觉得这东西就是个赔钱玩意儿。要是我写了测试,又没测出 bug 来,那写测试不就亏大了么?干啥浪费这个时间呢?
首先要知道两个大事实:人员会流动,应用会变大。因此,意图依赖人、依赖手工的方式来应对响应力的挑战首先是低效的,从时间维度上来讲也是不现实的。因此,我们才需要一套自动化的、目标明确的套件,来帮我们快速反馈、守卫质量。这组套件,就是自动化测试。
其次,没有测试的代码就意味着不能重构,基本就只能看着代码腐化,没有任何持续改进的机会和氛围。有了自动化测试,你就有机会进行重构和自动化回归,才可能做持续改进。
为什么需要单元测试
为了达到高响应力这个目标,测试当然只是其中一个方式,稳定的自动化部署、集成流水线、良好的代码架构、组织架构的必要调整等,都是必须跟上的设施。在一篇单元测试的文章里谈这些未免宏大,暂且按下不表。即便谈自动化测试,也并非全部都是单元测试。
为了使自动化测试套件具备高响应力特征,组合多个层次的测试是必要的。它要依赖于各个项目具体的痛点、成本、资源等进行定制,成为一个测试策略。我们选择对单元测试进行主要覆盖,是因为它具备编写成本低、反馈速度快、保护单元成本高的特点,显然应该是策略中价值较高的部分,并且可以随着 TDD 成为产品代码的一部分被持续交付。但这不意味着测试层次中的其他部分成本高或反馈慢,或不应该写。
什么是好的单元测试
续 #200 所谈,加上「响应力」这个帽子,好的单元测试应该符合这两点大原则:
- 与被测单元功能唯一相关
- 表达力极强
与被测单元功能唯一相关
也就是说,一个测试应该当且仅当「对于给定输入,代码给出了期望输出」时才通过,当且仅当「对于给定输入,代码未能给出期望输出」时才挂掉。它仅与 输入(input) 与 输出(output) 两个变量相关,这使单元测试成为「功能正确性」的唯一回答者。唯如此,它才能快速、准确地对业务功能的变化做出响应。
要达到这条原则其实不很简单,比如举一些我们常见的反模式:
- 改动了不属于被测功能的代码,会使测试挂掉
- 比如 Angular 默认的 component 测试设施,改 view 可能使 component 组件的逻辑功能测试挂掉;
- 比如被测单元有依赖,另一个类挂掉导致一个被测单元挂掉;
- 期望的输入输出不变,改动了实现,会使测试挂掉 - 测试依赖了实现细节,从而无法支撑重构
- 比如 redux-saga 官方推荐的
cloneGenerator
测试设施 - 比如断言实现过程产生的中间变量
- 比如测试
private
方法等
- 比如 redux-saga 官方推荐的
表达力极强
表达个什么东西呢?大意三点:
- 决定输出的输入是什么
- 业务是什么
- 输出是什么
与此三点无关的元素需要减到最少,比如无关的测试数据、无效的测试描述等。当这个测试挂掉时,它必须能清楚告诉你:在这个业务场景下,对于这个输入,我期望 A 输出,然而被测方法给了 B 输出,A 与 B 在数据内容、结构上的差别是什么。
参考
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论