12

前端单元测试实践

 3 years ago
source link: https://zhuanlan.zhihu.com/p/55960017
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前端单元测试实践

浙江天猫技术有限公司 前端技术专家

一说到单元测试,可能对于业务一线同学来说,心理立马就会无形中有一种压迫感,心想 “业务都做不完了,写个球的单元测试,先保证功能完备,赶紧上线才是王道”,这句话的核心是以业务为重,没任何问题,但是,业务在任何时候都是重要的,除了业务,其实还有效率。

没有效率,就没有生产力,没有生产力就没法给业务铺垫更广阔的道路,效率如此重要,那我们该从哪些维度来提升效率呢?从笔者的个人经验来看,不管是在什么领域,我们在提效道路上一定会经历以下几个阶段:

  • 规范标准化
  • 机器自动化
  • 系统平台化
  • 人工智能化

要经历以上过程,必须要有代码质量的保证,如果我们不关注代码质量,我们的研发效率是没法做到质的飞越的,原因很简单,就是人类在解决各种问题的过程中,总会不由自主的引入其他问题,从而导致系统稳定性降低,如何在漫长的系统维护过程中,保证每次发布的代码质量则是我们一直在持续探索的方向。所以现在软件测试在高校里都有专门的学科,同时软件测试岗位在互联网公司里也是非常常见的,可见,企业对系统稳定性的要求是非常非常高的。说了那么多,下面直接进入正题。


什么是单元测试?

单元测试,是指对软件中的最小可测试单元进行检查和验证,也就是说一个测试单元往往是一个原子型函数,同时,单元测试的编写者必须是作者本人,拥有单元测试的程序有以下几个好处:

1、它是一种验证行为

程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支援。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。

2、它是一种设计行为

编写单元测试将使我们从调用者观察、思考。特别是先写测试(Test First),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。

3、它是一种编写文档的行为

单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。

4、它具有回归性

自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。


单元测试用例设计

任何一个单元测试都应该包含:

    • 离散覆盖参数值域
    • 最大值验证
    • 入参数据类型非法
    • 内存溢出验证

幂等

对于单元测试来说,保证其幂等性非常重要,幂等就是在相同输入的前提下,其输出结果不随时间而改变。

所以,我们可以看到,对于函数式编程语言来说,写单元测试则是非常容易的事情,因为在函数式范式中,我们的函数都是纯函数,在范式层面上就已经约束了开发者写出幂等的程序,那么,在javascript领域,我们想要写出质量更高,对测试友好的代码的话,则需要尽可能的写出各种纯函数,从而保证幂等性。

对于前端而言,其实还包含UI界面的幂等,如何更加高效的保证界面幂等,我们是可以借助jest的快照能力实现html结构级别的幂等验证或者通过gemini的离线截图能力来实现像素级的幂等验证。

Mock

  • Mock数据,在编写单元测试用例的过程中,构造Mock数据是非常重要的实现手段,因为构造数据就是我们在构造输入的过程,比如正常输入/边界输入/非法输入
  • Mock环境,对于前端自动化测试而言,我们的环境Mock,往往是通过jsdom之类的库实现环境mock,保证离线场景下可以验证依赖浏览器API的程序逻辑
  • Mock事件,对于离线场景来说人机交互事件是不会有真实人类参与的,所以,我们需要Mock人机交互事件,帮助程序逻辑实现UI界面的交互功能性测试,在React中,是可以通过enzyme来实现Mock事件
  • Mock模块/第三方包,有些场景我们的程序依赖了某些第三方包,但是第三方包会引入副作用,比如axios,如果被测试的程序使用了该模块,它会走真实的发请求逻辑,这样还需要开一个mock请求服务,如果有一个模块拦截Mock能力,我们就不需要再开一个mock请求服务了,恰好jest提供了模块mock的能力,对于这类问题便可以轻松解决。
  • Mock函数/类,在Javascript语言中,函数的入参同样也可以是函数(匿名函数),这恰好是Js最灵活的地方,但是如果参数是函数,则会使得测试用例的编写难度大大提升,我们很难知道入参函数的调用情况,所以,如果我们可以跟踪入参函数调用情况,就能很轻松的验证函数式编程范式下的程序逻辑,恰好jest提供了一个函数Mock能力,可以帮助用户快速Mock一个可以跟踪其调用情况的匿名函数。同样,对于类也是,jest提供了mock类的能力,帮助用户跟踪一个类实例的使用过程。

白盒覆盖

白盒覆盖就是测试用例要尽可能的覆盖程序内部的所有分支语句,从而整体性的保证代码质量。

我们都知道,覆盖率是衡量单元测试质量的核心指标,但是,对于TDD而言,我们肯定不可能做到一开始就达到100%的覆盖率,所以,正常的单元测试用例,往往是先从黑盒用例来写,也就是程序对外暴露的API层面的测试,前期先将这部分的单测覆盖全,后期,我们在bugfix或者feature addtion的过程中可以逐步增加测试用例,最终逐步达到80%以上的覆盖率即可满足白盒覆盖的效果。

单测定级

根据我们前面所述的白盒覆盖,覆盖率是一个非常客观的指标,但是覆盖率对于开发者的认知模型而言是不够清晰结构化的,所以,我们还需要对覆盖率再做一次结构化定级,方便开发者一步步完善单元测试,下面让我们来枚举一下所有的单测级别:

  • Level1:正常流程可用,即一个函数在输入正确的参数时,会有正确的输出
  • Level2:异常流程可抛出逻辑异常,即输入参数有误时,不能抛出系统异常,而是用自己定义的逻辑异常通知上层调用代码其错误之处
  • Level3:极端情况和边界数据可用,对输入参数的边界情况也要单独测试,确保输出是正确有效的
  • Level4:所有分支、循环的逻辑走通,不能有任何流程是测试不到的
  • Level5:输出数据的所有字段验证,对有复杂数据结构的输出,确保每个字段都是正确的


自动化单元测试

其实前面已经提到过了,Jest,就是一款自动化单元测试解决方案,它基本上满足了前端单元测试的所有测试需求,而且它还是一款零配置解决方案,顾名思义,就是最简单场景下是无需任何配置即可快速编写测试用例。所以使用Jest,前端写测试用例就变得十分容易了。本文不会介绍具体jest该如何使用,它有哪些API,因为此类文章到处都能找到,本文更多的是从测试方法论出发探讨单元测试的实施方案。


提高测试用例编写效率

有了Jest,我们在写单元测试用例的配置成本已经很低了,所以,单元测试的成本,更多的是编写测试用例上,

要提高测试用例编写效率,我们主要从几个方向来提高:

  • 定制标准用例模板,让开发者做填空题,而非选择题
  • 制定单元测试开发规范,帮助开发者写出统一一致的单元测试用例,也方便后续协同开发维护
  • 渐进式编写测试用例,借助bugfix/feature addtion过程逐步完善测试用例,最大化减轻前期时间压力


React组件单元测试规范

1. 测试文件统一在src/__tests__目录中维护

主要是Follow Facebook的目录命名规范

2. 测试文件命名与React组件命名保持一致,后面以.spec.js结尾

主要是Follow Facebook的测试文件命名规范,比如:Form.spec.js

3. 测试用例使用test("功能描述",()=>{})函数描述用例单元

针对最小功能单元的测试用例主要集中在该函数内

4. 一组功能集合测试使用describe("功能集合描述",()=>{})函数描述功能集合

一个测试文件只能描述一个功能集合,这个功能集合可以是一个React组件,也可以是一个cjs模块

5. UI测试套件统一使用enzyme

使用enzyme可以借助jquery like的选择器方便的对DOM渲染结果做校验

6. React组件测试用例必须包含

  • API属性覆盖性测试用例
  • DOM快照比对,幂等校验
  • 私有Utils函数测试用例,千万不能忽略Utils函数的测试用例,很多时候,bug就出在这上面

7. 对DOM结构做用例校验

一个标准的React组件测试用例的输入往往是组件配置或交互事件,输出则是具体的DOM结构,我们的用例校验也都是对DOM结构做用例校验

8. bugfix/feature addtion必须要有对应的单元测试用例才能发布

9. 团队协作,MR/PR必须要有对应的单元测试用例才能发布


参考资料

https://jestjs.io jest官网

https://github.com/sapegin/jest-cheat-sheet jest快速上手教程

https://github.com/jest-community/awesome-jest jest周边生态汇总

https://zhuanlan.zhihu.com/p/28162082 jest中文教程

http://testanything.org/tap-specification.html TAP测试报告规范


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK