今天看啥  ›  专栏  ›  xiaoT

用 Jest 和 Enzyme 测试 React I

xiaoT  · 掘金  ·  · 2020-04-21 02:56
阅读 17

用 Jest 和 Enzyme 测试 React I

原文地址: medium.com/codeclan/te…
译文地址:github.com/xiao-T/note…
本文版权归原作者所有,翻译仅用于学习。


这篇文章将会介绍如何设置并使用 Jest Enzyme 测试通过 Create React App (CRA) 创建的 React 应用。对于那些从头开始的人我们会给出一些建议。但是,不会涉及太多有关 React 的知识。

Jest 和 Enzyme 是两个不同,但又相互相成的工具,整合在一起可以提供更加灵活和更具创造性的测试能力。我们将会简单介绍两者有什么差异。

Jest

Jest 是一个 Javascript 单元测试框架,Facebook 用来测试服务和 React 应用。

CRA 已经内置了 Jest;不再需要单独安装。

Jest 是一个 测试运行器断言库模拟库

Jest 也提供 快照测试,它可以创建一个组件的快照,然后与上一次保存快照对比。如果,前后两者不匹配,测试就会失败。快照将会被保存在名为 __snapshots__ 文件夹中,它与被测试的文件在同一目录中。快照看起来就像下面这个样子:

exports[`List shallow renders correctly with no props 1`] = `
<List
  ordered={false}
>
  <ListItem>
    Item 1
  </ListItem>
  <ListItem>
    Item 1
  </ListItem>
</List>
`;
复制代码

快照测试必须结合浏览器测试,并且,在一开始就要校验快照,以保证快照能按照期望的输出。

Enzyme

Enzyme 是针对 React 的测试功能库,它可以更容易的断言、操控和遍历 React 组件。

Enzyme,由 Airbnb 创建,并添加了更好的功能方法,比如:渲染组件查找元素与元素交互

在 CRA 中它必须单独安装。

Jest 和 Enzyme

  • Jest 和 Enzyme 两者都可以测试 React 应用,Jest 还可以测试其他的 Javascript 应用,但是 Enzyme 只能测试 React。
  • Jest 可以单独渲染组件和快照测试,Enzyme 只是添加了更简单的方法。
  • 没有 Jest,Enzyme 也可以使用,但是必须结合另外的测试运行器(runner)。

综上所述:

  • Jest 作为测试运行器,断言库和模拟器使用
  • Enzyme 提供额外的测试功能用与交互。

设置

安装和配置

如果不使用 CRA,需要安装 Jest:

npm install --save-dev jest babel-jest
复制代码

安装 Enzyme:

npm install --save-dev enzyme enzyme-adapter-react-16 enzyme-to-json
复制代码

更新 package.json

"jest": {
  "snapshotSerializers": ["enzyme-to-json/serializer"]
}
复制代码

相比 Enzyme,为了更加方便的对比快照,enzyme-to-json 提供了更好组件格式化方式。当使用快照时 snapshotSerializers 可以更大限度的压缩重复代码。如果没有指定序列化工具,测试中每次对比快照是否匹配时都需要把组件传递给 enzyme-to-json.toJson() 方法,反之,就不需要。

expect(toJson(rawRenderedComponent)).toMatchSnapshot();
复制代码

通过在 package.json 中添加这一配置,在你调用 Jest 的 .toMatchSnapshot() 时就不需要在调用 JSON 格式化方法了。

./src/setupTests.js 创建 setupTets.js 文件:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';configure({ adapter: new Adapter() });
复制代码

CRA 会自动搜索这个文件,如果,你没有使用 CRA,这时你就需要在 snapshotSerializers 的相同位置添加这一配置:

"setupFiles": ["./src/setupTests.js"],
复制代码

创建测试文件

Jest 将会在符合以下规则的地方查找所有的测试文件

  • 文件夹 __tests__ 中以 .js 结尾的文件
  • .test.js 结尾的文件
  • .spec.js 结尾的文件

比较方便的是把每个测试文件和需要测试代码放在同一目录下。这在语义上更加有意义,同时也会让相对路径更加简单(./MyComponent vs ../../MyComponent )。

以下是 MyComponent.test.js 的演示:

import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
  it('should render correctly in "debug" mode', () => {    const component = shallow(<MyComponent debug />);
  
    expect(component).toMatchSnapshot();
  });
});
复制代码

在 CRA 环境下,当执行 npm test 时会运行所有的测试文件并把结果输出到终端。通过自定义标签 -- --testPathPattern filename/ 可以指定只运行特定的文件或者使用 -- --testPathIgnorePatterns filename/ 来忽略指定的文件。

Mount, Shallow, Render

import { mount, shallow, render } from ‘enzyme';
复制代码

为了测试组件以上的方法肯定会用到,就如上一段代码所示。

Mounting

  • 完整的 DOM 渲染,包括子组件
  • 对于需要和 DOM 交互、完整测试组件生命周期情况,这是最理想渲染方式
  • 因为是真实把组件渲染成 DOM,为了不影响其他的测试,每次测试完成都需要调用 .unmount()
  • 允许直接访问传递给 root 组件(包括默认的)和传递给子组件的 props。

Shallow

  • 只渲染单个组件,不包括子组件。这对于需要隔离组件做单纯的单元测试非常有用。它可以防止因为子组件的改变和 bugs 影响组件测试的结果。
  • 在 Enzyme 3 中 shallow 渲染默认也可以访问组件的生命周期方法。
  • 不能访问传递给 root 组件的 props(因为它们不是默认 props),但是,它们可以传递给子组件,也可以测试传递的 props 是否对组件有影响。也就说 shallow 可以测试组件的渲染,但不能测试其中的 element。

Render

  • 把组件渲染成静态的 HTML,包括子组件
  • 不能访问 React 的生命周期方法
  • 相比 mount 成本更低,但功能也更少

Testing

基本的组件渲染

一个简单无交互的组件:

it('should render correctly with no props', () => {
  const component = shallow(<MyComponent/>);
  
  expect(component).toMatchSnapshot();
});
it('should render banner text correctly with given strings', () => {
  const strings = ['one', 'two'];
  const component = shallow(<MyComponent list={strings} />);
  expect(component).toMatchSnapshot();
});
复制代码

事件

Enzyme API 有好几种方式可以模拟事件或者用户交互。如果,你想测试子组件的交互功能,这时就需要 mount 方法了。

it('should be possible to activate button with Spacebar', () => {
  const component = mount(<MyComponent />);
  component
    .find('button#my-button-one')
    .simulate('keydown', { keyCode: 32 });
  expect(component).toMatchSnapshot();
  component.unmount();
});
复制代码

模拟方法

或许你只是想简单验证下通过 prop 传递的方法是否成功执行。

const clickFn = jest.fn();
describe('MyComponent', () => {
  it('button click should hide component', () => {
    const component = shallow(<MyComponent onClick={clickFn} />);
    component
      .find('button#my-button-two')
      .simulate('click');
    expect(clickFn).toHaveBeenCalled();
  });
});
复制代码

慢慢会变得更加复杂,你或许想模拟 MyComponent.js 里面 import 的一个方法,它返回一个值,检查是否被调用,并对比快照。

想象一下,我们在 MyComponent.js 中引入了一个 SaveToStorage 对象,它包含 TryGetValueTrySetValue 两个方法。TryGetValue 有一个默认的返回值:false,如果,它返回 true 说明组件会改变。组件中两个不同按钮会使用它们。

我们用 jest.mock 也可以模拟这种行为,同时,jest.fn 可以覆写组件内的方法。

const mockTryGetValue = jest.fn(() => false);
const mockTrySetValue = jest.fn(); 
 
jest.mock('save-to-storage', () => ({   
  SaveToStorage: jest.fn().mockImplementation(() => ({
    tryGetValue: mockTryGetValue,
    trySetValue: mockTrySetValue,
  })), 
}));
describe('MyComponent', () => {
  it('should set storage on save button click', () => {
    mockTryGetValue.mockReturnValueOnce(true);
    const component = mount(<MyComponent />); 
    component.find('button#my-button-three').simulate('click');
    expect(mockTryGetValue).toHaveBeenCalled();
    expect(component).toMatchSnapshot();
    component.unmount();   
  });    
});
复制代码

总结

在这,只介绍了其中一部分,Jest 还有其它的断言方法(包括:toEqual),同时 Enzyme 还有更多的遍历和交互的方法(包括:firstsetProps)。每个文档介绍的都很好,并提供了更多的可能。

同时使用 Jest 和 Enzyme 让测试 React 组件更加容易,同时测试更具可读性。

多谢你们喜欢!🙂

如果,你喜欢这篇文章,你还可以看看这些:

资源

除了官方文档和我自己的经验,我发现下面两篇文章也非常有用,并且,我写的这篇文章也受到它们的启发同时也巩固了我的知识:

除单元测试和集成测试之外,你还希望看看视觉回归测试,我推荐你关注 Differencify




原文地址:访问原文地址
快照地址: 访问文章快照