Jest + Enzyme React 组件测试实践

Posted 小章鱼哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jest + Enzyme React 组件测试实践相关的知识,希望对你有一定的参考价值。


最近把组件测试接入到日常开发,提高了项目代码健壮性,可维护性。本人也从0到1收获了组件测试的经验。

本文总结一下最近两周 组件测试 相关的研究,包括:

  • Jest + Enzyme 的基本介绍
  • Jest + Enzyme 的实践
  • Jest 原理浅析
  • Jest 生态 & 未来

文章目录

为什么选择Jest & Enzyme?

1. Jest

Jest 是一套Facebook家背书的测试框架。

实际上,编写一个测试用例,我们需要以下准备:

  1. 一个测试类库
  2. 一个断言库(assertion)
  3. 一个测试执行环境(environment)
  4. 其他(function or module mock, snapshot etc…)

Jest是包含以上(1234)四合一的一款测试框架。

  • Jest内置了jsDOM和Node的执行环境。默认开启面向浏览器端的jsDOM环境。
  • Jest内置一套断言库,和Jasmine相同语法。
  • Jest自带函数和模块的Mock能力;自己发明了一个专门针对组件测试的快照(snapshot)测试;Jest支持热更新;Jest支持多线程测试。

当然Jest也有一些劣势。

2019年另一款很火的测试框架Mocha,自身不带断言库,也不带Func/Module Mock,集成社区相对成熟的断言库Chai和mock库SinonJS之后,非常强大。Chai断言库相较于Jest内置的断言库API更加丰富更加强大,SinonJS的mock的API也十分完善。而且Mocha的不带其他杂七杂八功能的理念,让它十分具有可配置性。

但是最终选择Jest原因有以下几点:

1. 没有最牛逼的框架,只有最合适的框架。

  • 对于组件库的项目,我们对于组件测试的需求非常大,包括组件API的测试和组件呈现效果的测试。Jest自带的snapshot的测试,很亮眼,会把组件渲染的DOM解析成JSON作为快照保存,下一次再获得新的DOM的JSON序列,与旧的序列进行对比,保证组件的呈现的稳定性。
  • 组件库的测试很少涉及到网络请求,server端测试,目前是一个纯浏览器端的测试需求。所以Jest自带的断言能力,mock能力够用。而且据我所知,Jest是有一个叫做jest-extend的断言库扩展包。甚至,自己手写就好了。

2. 更简单的配置,更快地上手。

  • 实现一个snapshot,只需一句话:expect(component).toMatchSnapshot(),很简单。
  • 断言库来自Jasmine,很精炼,够用,简单。

3. 实际上Jest的可配置化也非常强。

  • Jest有自己的生态建设(Jest Community)。目前可以配置更加强大的断言扩展,更加灵活的运行环境,甚至还有snapshot使用svg保存的拓展(实际上Jest有倾向做真正的视觉测试,像素比较???好酷鸭)
  • Jest自身的配置文件可配置项非常多。我就通过Jest的config配置文件把一个storybook文章生成文件mock成测试用例了哈哈(理论上讲,任何文件都可以变成测试文件)。

4. MVVM框架支持度

  • 目前Jest支持市面上主流的开发框架:Vue,React,Angular。babel支持的就是它支持的,因为它提供transform功能。使用jest-babel插件然后利用本地babel配置做文件转换皆可。(也就是说,理论上讲,支持市面上拥有babel转换插件的任何MVVM框架)。

2. Enzyme

Enzyme是Airbnb家的React组件用例测试渲染库。

实际上React官方对于React components的测试有暴露一些库:

  1. react-dom/test-utils: 拥有一些看一遍也不知道怎么用的API。
  2. react-test-render:可以无渲染环境的基础下,通过shallow渲染拿到WHATWG规范的DOM树结构。React团队利用这个库和Jest团队一起努力打造了React的snapshot test。

Enzyme底层就是封装了以上两个库,却拥有简单精巧的API,打败了React家自带的test render(API设计的力量!React家也一直在安利Enzyme…)。它一共有三个大API:

  • shallow:浅渲染。只渲染根级,不渲染子集,它就是封装的react-test-render/shallow,生成的是虚拟DOM。
  • mount:完整DOM渲染。拥有DOM API交互能力。
  • render:静态渲染。用来分析html结构。snapshot请使用这个API。

Enzyme对于以上三种渲染方式都有详细介绍的小API查询。

最终选择Enzyme原因很明确了,这是目前为止最简单上手,功能最强大的React components render库了。

Jest + Enzyme 的实践

1. 配置开发

2. 调试

Jest一些十分有用的Config:

3. Transform

Jest实际上可以测试任意文件:只要你的文件可以通过transform,得到的结果可以被Jest的断言库语法解析。因此一个Markdown,txt等等文件都可以被测试。

4. Mock

Jest默认会mock所有node_modules目录下的依赖文件。我们既可以在config配置文件中配置全局mock规则,也可以在测试用例编写的时候mock某一个function或module。
Jest在内部是这样处理的:拿到file code之后,包一层函数,传入重写的require,export函数。把mock的代码接在code前面。然后一起执行?。

Jest 原理浅析

Jest一共有30多个package,从零开始看你会疯掉。如果想要研究的话,推荐看去年底,Jest官方发布的架构原理视频:https://jestjs.io/docs/en/architecture, 粗略地讲了其中几个重要的包的功能,以及一个测试用例如何被初始化,添加Mock,调度,最终执行的过程。核心内容就是下面这张图。

1. Jest的本质与Jasmine

Jest本身其实是基于Jasmine库之上封装的,Jest的断言库和具体的测试用例执行过程都是Jasmine做的。Jest本身做的是以下事情:

  • CI封装
  • code transform
  • 更抽象的project config
  • 多线程调度通信
  • jsdom和node以及可以灵活配置的envrionment
  • snapshot

(现在还会出现栈溢出的问题,对此Jest的做法是加一个100ms的timeout解决我的天。我在用Jest的时候,snapshot稳稳栈溢出,需要加一个enzyme-to-json/serializer解决。

Jasmine是BDD(behavior-driven development行为驱动开发)的javascript测试框架。Jasmine非常轻量,本身只有20KB。Jest目前的断言语法都是来自于Jasmine。它定义有如下两种语法:

  1. describe:内部定义为一个Suite。是一堆测试块的集合。
  2. it:内部定义为一个Spec。是一个测试块。

以下是一段测试用例代码:

describe("A suite", function() 
  it("contains spec with an expectation", function() 
    expect(true).toBe(true);
  ););

非常语义化,不用看API都能看懂,类似Regular English,这就是BDD。

它拿到用户配置,首先会正则匹配到所有符合要求的文件,抽取出一颗树processTree,因为它支持Suite的嵌套,所以它会先遍历这棵树,做一次order更新,然后用捕获冒泡的方式从树根遍历到树叶再从树叶的father节点再遍历到树根,在它的context下执行测试用例。目的是把测试report通知到所有祖先Suite,最后打出一份Report。(但是,你不要以为Jasmine体积小就好阅读了,Jasmine的源码就是一坨回调地狱。我认为它可以选择重构。)

2. Jest Own Test Runner与Flux

Jest独立出来的其中一个包,就是图中所说的jest-circus。我们可以通过配置选择circus而不是jasmine, 它将要替代Jasmine???

Circus is a flux-based test runner for Jest that is fast, easy to maintain, and simple to extend.

官方说Circus更加快,好维护,易拓展。它支持用户接入任何自定义的执行环境。

import NodeEnvironment from 'jest-environment-node';
import Event, State from 'jest-circus';

// 自定义的环境
class MyCustomEnvironment extends NodeEnvironment 
  //...

  handleTestEvent(event: Event, state: State) 
    if (event.name === 'test_start') 
      // ...
    
  

它使用了Flux的思想,自己维护了一个state,然后通过事件的Emitter去更新state,没有回调地狱的存在。所以它说:

Mutating event or state data is currently unsupported and may cause unexpected behavior or break in a future release without warning. New events, event data, and/or state data will not be considered a breaking change and may be added in any minor release.

它只支持不可变数据和函数。

Jest 生态 & 未来

Jest有自己的平台https://jestjs.io/docs/en/jest-platform,提供了一些工具函数。

未来的话,Jest有倾向:

  1. 在snapshot的基础上做视觉测试。
  2. 多项目测试。比如同时用jest起一个server端和browser端的测试。
  3. 解决测试时间问题。

以上是关于Jest + Enzyme React 组件测试实践的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jest / Enzyme 在 React 中的功能组件内部测试方法

使用 Jest/Enzyme 在 React 功能组件中测试封闭组件

使用 Jest 和 Enzyme 测试使用 Redux 的功能性 React 组件

使用 React Suspense 和 React.lazy 子组件进行 Jest/Enzyme 类组件测试

如何覆盖 react-hooks 组件内的函数测试 - Enzyme/Jest

Jest/Enzyme 使用 try/catch 中的 async/await 方法测试 React 组件