乐趣区

关于jest:GrowingIO-Design-组件库搭建之单元测试

前言

GrowingIO Design 是用 React 编写的组件库,实质上就是 React 组件,你能够用像测试其余 JavaScript 代码相似的形式测试 React 组件。当初有许多种测试 React 组件的办法。大体上能够被分为两类:

  • 渲染组件树: 在一个简化的测试环境中渲染组件树并对它们的输入做断言查看。
  • 运行残缺利用 :在一个实在的浏览器环境中运行整个利用。

咱们对于第一类称之为单元测试,本文次要专一于这种状况的测试策略;第二类称为端到端(end-to-end)测试,残缺的端到端测试在避免对重要工作流的屡次回归方面很有价值,对于组件的端到端测试会有专门一篇文章来介绍。

工具调研

对于测试工具的抉择,次要从 2020 年的 State of JavaScript Survey 中列出的工具筛选。咱们先从“应用度”和“满意度”两个角度来看测试生态圈的比拟常用工具。


测试工具应用度比照

测试工具满意度比照

Jest、Storybook 在“应用度”和“满意度”上都取得了比拟高的分数,新进入的 Testing Library 在“满意度”上也取得了高分。

注:
满意度:会再次应用 / (会再次应用 + 不会再次应用)
应用度:(将再次应用 + 将不再应用) / 总计

除了以上的几款工具,看看其余工具的用户百分比:

工具抉择

先看看 React 官方网站(目前最新版本 v17.0.2)举荐的工具:

  • Jest 是一个 JavaScript 测试运行器。它容许你应用 jsdom 操作 DOM。只管 jsdom 只是对浏览器工作体现的一个近似模仿,对测试 React 组件来说它通常也曾经够用了。Jest 有着非常优良的迭代速度,同时还提供了若干弱小的性能,比方它能够模仿 modules 和 timers 让你更精密的控制代码如何执行。
  • React Testing Library(简称:RTL)是一组能让你不依赖 React 组件具体实现对他们进行测试的辅助工具。它让重构工作变得轻而易举,还会推动你拥抱无关无障碍的最佳实际。尽管它不能让你省略子元素来浅(shallowly)渲染一个组件,但像 Jest 这样的测试运行器能够通过 mocking 让你做到。

实际上在 v16.7.0 版本的网站上,他们还举荐了另一个工具:

  • Enzyme 是 React 的 JavaScript 测试实用程序,能够更轻松地测试 React 组件的输入。你还能够操作、遍历和以某种形式模仿给定输入的运行时。Enzyme API 旨在通过模拟 jQuery API 来进行 DOM 操作和遍历,从而变得直观和灵便。

先说说 Jest,Jest = jsdom + Mocha + Sinon + Chai,从这里能够看出 Jest 的弱小,而且它还是开箱即用的。

从 2018 年甚至更早,咱们始终应用 Jest + Enzyme 来编写的单元测试,然而到 2020 年了,咱们须要从新扫视一下,抉择一个目前来说适合的工具。后文次要拿 RTL 做比照后,先从历史说起。

一点历史

  • 2016 年 3 月 2 日,Airbnb 官宣了 Enzyme——一个用来测试 React 组件的 JavaScript 库。在之前的三个月中,Enzyme 在 GitHub 上播种的 3000 个 Stars,50 个贡献者,其中有 45 个不是 Airbnb 的员工。
  • 2018 年 3 月 15 日,Kent C.Dodds 在社交网站上公布他想做一个更轻量的测试库来代替 Enzyme。同年 4 月 2 日,Kent C.Dodds 在本人的博客上官宣了 React Testing Library——一个更轻量的 React 测试库,集成了 react-dom 和 react-dom/test-utils 的工具函数,提倡更好的测试实际。
  • 2019 年 2 月,React 团队公布了期待已久的新版本 React(v16.8),该版本大幅扭转了 API 并引入了 React Hooks。然而 Enzyme 并不反对独自测试 Hooks,而 RTL 能够通过独自库来反对。
  • 2020 年 2 月 28 日,Airbnb 发表他们将 Enzyme 的所有权转移到了一个内部 GitHub 组织。尽管他们承诺持续反对 Enzyme,但他们也提到 RTL 在他们的我的项目中越来越受欢迎。
  • 2021 年,目前只有一名开发人员在保护 Enzyme——Jordan Harband。他是一位多产的开源贡献者,是 TC39 委员会的成员(他们定义 JavaScript),并且是一位真正的英雄,他单独保护着这款为寰球数百万个测试套件提供反对的工具。

npm trends

上图是 Enzyme 和 RTL 在两年内下载量的趋势,大概在 2020 年 9 月,RTL 的下载量实现反超,并在当前的一年里一骑绝尘。

当初看来,整个业界仿佛曾经转向 RTL 了。

Talk is cheap, Show me code

假如咱们当初有 Toggle 这样的一个组件:

class Toggle extends React.Component {constructor(props) {super(props); 
    this.state = {isToggleOn: true}; 
 
    this.handleClick = this.handleClick.bind(this); 
  } 
 
  handleClick() { 
    this.setState(state => ({isToggleOn: !state.isToggleOn})); 
  } 
 
  render() { 
    return (<button onClick={this.handleClick}> 
        {this.state.isToggleOn ? 'ON' : 'OFF'} 
      </button> 
    ); 
  } 
} 

Toggle 组件的测试用例至多蕴含以下两个:

  1. 当 Toggle 处于 ON 状态时,点击后变成 OFF 状态;
  2. 当 Toggle 处于 OFF 状态时,点击后变成 ON 状态;
    应用 Enzyme 来实现,如下:
describe("Toggle", () => {
  let component;  
  beforeEach(() => {component = mount(<Toggle />);
  });

  it("can turn off when is on", () => {component.instance().handleClick();
    expect(component.state().isToggleOn).toEqual(false);
  });
  
  it("can turn on when is off", () => {component.setState({ isToggleOn: false});
    component.instance().handleClick();
    expect(component.state().isToggleOn).toEqual(true);
  });
});

应用 Testing Library 来实现,如下:

describe("Toggle", () => {it("can turn off/on", async () => {const { container} = render(<Toggle />);
    fireEvent.click(container);
    expect(container.innerHTML).toEqual("OFF");
 
    fireEvent.click(container);
    expect(container.innerHTML).toEqual("ON");
  });
});

小结

借助 RTL,你能够轻松地编写测试,这些测试充沛代表了用户如何体验应用程序。比方说,当你用 RTL 编写测试时,你在测试你的应用程序,就像你是与利用程序界面交互的用户一样。

另一方面,当你用 Enzyme 写测试时,即便你也能达到与 RTL 雷同的自信程度,以相似于实在用户的形式构建测试构造有点麻烦。

另外的一些实际

快照测试

快照测试是 Jest 的个性,来自 Jest 官网的介绍:

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.

你能够应用测试渲染器疾速为 React 树生成可序列化的值,而不是渲染图形 UI,可能须要构建整个应用程序。看一个具体的例子:

import React from 'react'; 
import renderer from 'react-test-renderer'; 
import Link from '../Link.react'; 
 
it('renders correctly', () => { 
  const tree = renderer 
    .create(<Link page="https://www.growingio.com">GrowingIO</Link>) 
    .toJSON(); 
  expect(tree).toMatchSnapshot();});

Link 组件的实现查看:https://github.com/facebook/j…

运行 Jest 后,将会生成快照文件,相似这样:

exports[`renders correctly 1`] = ` 
<a 
  className="normal" 
  href="https://www.growingio.com" 
  onMouseEnter={[Function]} 
  onMouseLeave={[Function]} 
> 
  GrowingIO 
</a> 
`; 

快照工件应与代码更改一起提交,并作为代码审核过程的一部分进行审核。
接下来看看 Jest 官网给出的三点最佳实际:

  1. Treat snapshots as code(将快照视为代码)
  2. Tests should be deterministic(测试应该是确定性的)
  3. Use descriptive snapshot names(应用描述性快照名称)

依据咱们在 GrowingIO Design 中的实际,不须要为每个组件以及组件的每种状态都生成快照。有以下两点起因:

  • 大多数状况下,批改组件的 CSS 款式名,减少或者删除 Dom 元素,尽管扭转了组件的构造然而组件样子并没有扭转,这时候就得去更新快照,减少代码审核的工作量。
  • 快照测试通过几行代码就能实现,对代码行测试覆盖率有显著的进步,然而这种测试品质并不高。而且会减少测试命令执行的工夫。

Storybook

在《GrowingIO Design 组件库搭建之组件开发》一文中提到组件的文档工具,应用 Storybook 来编写组件 Demo,这些 Demo 可在单元测试工具中重复使用。每个 Demo 在不依赖 Storybook 的状况下都是“可渲染的”。这意味着任何的测试框架也将可能渲染 Demo。
以下是如何在 RTL 中应用它的示例:

import React from 'react'; 
import {render, screen} from '@testing-library/react'; 
import {Primary} from './Button.stories'; 
 
it('renders the button in the primary state', () => {render(<Primary {...Primary.args} />); 
  expect(screen.getByRole('button')).toHaveTextContent('Primary'); 
});

总结

最看重的一点是 RTL 专一于测试用户体验——毕竟,这才是真正重要的。咱们的客户不会看到咱们的组件有什么 props,或者它们是数组还是对象,客户只关怀它是否失常工作——并且会帮忙你在将来交付价值。

React 的将来在于基于函数的组件、React Hooks、异步渲染,而这些个性明天最好搭配 RTL 应用。纵观 Enzyme 过来三年的倒退历程,它仿佛不太可能赶上所有这些个性,同时还能解决其余那么多问题。

更值得一提的是,应用 RTL 有助于进步组件库的无障碍性(Accessibility,简称 a11y)。对于组件库无障碍性,前面会用一篇文章来具体介绍。

参考

GrowingIO Design:https://github.com/growingio/…
State of JavaScript Survey:https://2020.stateofjs.com/zh…
Testing Overview:https://reactjs.org/docs/test…
Test Utilities:https://5c54aa429e16c80007af3…
Enzyme:https://enzymejs.github.io/en…
Enzyme: JavaScript Testing Utilties for React:https://medium.com/airbnb-eng…
Enzyme‘s next Phase:https://medium.com/airbnb-eng…
Jordan HarBand:https://github.com/ljharb
Snapshot Testing:https://jestjs.io/docs/snapsh…
Storybook Unit Testing:https://storybook.js.org/docs…
无障碍:https://developer.mozilla.org…

退出移动版