工具选择

enzyme vs  @testing-library/react

https://medium.com/enjoy-life-enjoy-coding/react-unit-test-%E7%82%BA%E5%9F%B7%E8%A1%8C%E7%B4%B0%E7%AF%80%E5%AF%AB%E4%B8%8B%E6%B8%AC%E8%A9%A6-%E7%BF%BB%E8%AD%AF-7bec3bca4ee1

enzyme更关注组件内部的具体实现,可以在测试中通过调用实例的方法,通过直接设置state和props来改变组件的状态来测试组件的状态改变是否带来符合预期的渲染变化。Enzyme 的实现有两个误报的风险:即使代码损坏,测试也会通过;即使代码正确,测试也会失败。

@testing-library/react 不关注组件的内部实现,更关注在用户的角度上通过可交互的方式来测试组件的渲染变化是否符合预期。

由于hooks写法在项目中越来越多,所以做不到通过实例来调用方法/设置state/props

@testing-library/react 在Sep 2020下载量就超过enzyme,并且目前周下载量大概是enzyme的1.5倍

怎么mock API

可以直接通过jest的munual mocks_进行模拟一个模块文件,但是需要在源码处新建一个__mocks__目录,如下图。我觉得有点测试代码侵入业务代码的嫌疑,所以没有采用
image.png
jest-mock-axios 在调用接口的时候在测试代码中mockResponse,比较方便,并且在测试代码中更改比较直观,如下图,另外在__mockData__目录下用一些ts文件来存储mock的数据,减少在测试文件中多次写数据结构。
image.png
image.png

忽略一些模块的具体逻辑

在jest-setup.ts初始化时忽略一些模块
主要是上报/bridge相关的模块,这些模块中的方法一般都调用第三方模块来进行,我们只需关心这些方法是否调用,不关心其内部逻辑

好测试的特征

准备输入数据、调用被测函数、断言输出结果。任何单元测试都可以遵循这样一个骨架,它是我们常说的 given-when-then 三段式。

单元测试的特征:

安全重构已有代码 -> 应该有且仅有一个失败的理由、不关注内部实现

保存业务上下文 -> 表达力极强

快速回归 -> 快、稳定

有且仅有一个失败的理由,这个理由是什么呢?是 「当输入不变时,当且仅当被测业务代码功能被改动了」时,测试才应该挂掉。为什么这会支持我们重构呢,因为重构的意思是,在不改动软件外部可观测行为的基础上,调整软件内部实现的一种手段。也就是说,当我被测的代码输入输出没变时,任我怎么倒腾重构代码的内部实现,测试都不应该挂掉。这样才能说是支持了重构。有的单元测试写得,内部实现(比如数据结构)一调整,测试就挂掉,尽管它的业务本身并没修改,这样怎么支持重构呢?不怪得要反过来骂测试成本高,没有用。一般会出现这种情况,可能是因为是先写完代码再补的测试,或者对代码的接口和抽象不明确所导致。

表达力极强,讲的是两方面

看到测试时,你就知道它测的业务点是啥,这些表达力体现在许多方面,比如测试描述、数据准备的命名、与测试无关数据的清除

测试挂掉时,能清楚地知道业务、期望数据与实际输出的差异

不要对断言进行if分支判断,不然不知道是断言出错还是代码出错。

不快的单元测试还能叫单元测试吗?那么为了达到快、稳定这个目标,我们需要:

隔离尽量多的依赖。依赖少,速度就快,自然也更稳定

将依赖、集成等耗时、依赖三方返回的地方放到更高层级的测试中,有策略性地去做

React单元测试策略

@connect和useSelector添加的属性如无意外不在测试组件时关注

组件分支渲染逻辑必须测

事件调用要测及其预期要测

纯 UI 可以直接使用快照测

纯UI组件JumpNumber测试用例如下,先检测初始数字的渲染,等待数字跳动结束后再检测到达数字的渲染。

如果组件使用了setTimeout一些定时器做动画,在测试用例中最好不要使用setTimeout去等待动画的结束,因为在测试中可能定时器任务不可控的情况导致等待结束但是动画还未结束的情况,所以最好是通过等待意料之中的UI变化后再进行断言
image.png

如果一个组件中的分支判断特别多,因为奖励不相同展示不同,点击响应不同。所以需要建立很多的用例来分别测试他们。

不要因为他们的逻辑其实差不多,但是有一些差别,就只使用一个用例来进行测试,然后在这个测试用例里做if判断进行断言,这样不直观,看测试代码的时候不知道是啥,并且仿佛是把业务逻辑重写一边。出错的时候不知道是测试代码出错还是业务代码出错。

reducer 作为纯函数,非常适合做单元测试,加之一般在 reducer 中做重逻辑处理,此处做单元测试保护的价值也很大。

测试model的步骤:

分别测试reducer和effect

初始化store,只对要测试的model进行初始化

调用effects方法

等待API方法被调用,然后使用mockAxios进行mock api的响应

劫持reducer方法,如果要调用toHanveBeen…方法检测方法是否在调用预期,需要被spyOn

等待reducer的调用,因为一般effect方法都是异步,需要确认effect调用后再对结果进行断言


一画先生
83 声望12 粉丝

我司长期招聘前端开发工程师,有意的小伙伴+vx: Mr_yihua