用 Jest 和 Enzyme 测试 React II

1,682 阅读4分钟

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


先前,我们已经知道如何设置 Jest 和 Enzyme,并且,顺便了解了 Jest 中如何模拟 ES 和 CommonJS 模块

在这篇文章中,我们将会看到更多的示例:如何通过 Enzyme 创建特殊的测试场景模拟组件中用户的交互。

如本系列第一篇文章所述,我们将会用 Enzyme 的 mount 的方法来实现组件的完整的渲染。来自文档的总结:

在需要组件与 DOM API 交互或者测试那些被高阶组件包含的组件时,完整的 DOM 渲染是最理想的方式

如果,你不打算在浏览器中运行测试,推荐使用 mount,它依赖 jsdom 库,这个库本质上是一个用 JS 实现的 headless 浏览器。

这让我们可以与组件交互,就像在浏览器中一样。

以下是基本的版本配置:

"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.3",

模拟事件

Enzyme 中模拟用户交互的语法非常简单直接,以下就是最简单的示例:

component.find(selector).simulate(event);

选择器

选择器 是下面的其中之一:

  • CSS 选择器
  • Prop 属性选择器
  • Prop 对象选择器
  • React 组件构造函数
  • React 组件 displayName

在这篇文章中,我们只会查看 CSS 选择器的几种不同方式。如果,CSS 选择器不能满足你的需求,可以继续查阅文档,Enzyme 的文档写的非常好,也有很好的演示。

通过文档我们知道 CSS 选择器支持以下几种:

  • 类选择器(.foo .foo-bar 等)
  • 元素选择器(input div span 等)
  • ID 选择器(#foo #foo-bar 等)
  • 属性选择器([href="foo"] [type="text"] 等)

以上语法也可以组合使用(button#id-foo)。

如果,父组件中存在多个需要交互的子组件时,一个常见的错误:Method "simulate" is only meant to be run on a single node. 3 found instead

这也很容易修复,你可以给需要交互的 node 指定索引:

// the initial
component.find([className="checkbox__input"]).simulate(event);
// becomes
component.find([className="checkbox__input"]).at(1).simulate(event);

事件

Enzyme 的方法 simulate 可以用来模拟 DOM 事件,常用的有:clickchangekeydown。选定 node 后,可以直接链式调用 simulate 来完成模拟交互:

component.find(selector).simulate('click');
component.find(selector).simulate('change');
component.find(selector).simulate('keydown', { keyCode: 32 });

编写测试用例

测试重新渲染

如果,DOM 事件引起组件重新渲染,卸载子组件,这时,Jest 快照测试就可以来验证组件是否正确的重新渲染。

it('should be possible to open menu with Spacebar', done => {
  const component = mount(<MyComponent />);
  component.find('#link-id').simulate('keydown', { keyCode: 32 });
  expect(component).toMatchSnapshot();
  component.unmount();
});

测试函数调用

如果,一个函数传递了子组件,你希望在父组件挂载的时候,验证它是否被正确的调用。首先,需要模拟这个函数,然后,以 prop 的形式传递下去。

const mockFunction = jest.fn();
it('should call mockFunction on button click', () => {
  const component = mount(
    <MyComponent onClickFunction={mockFunction} />
  );
  component.find('button#ok-btn').simulate('click');
  expect(mockFunction).toHaveBeenCalled();
  
  component.unmount();
});

测试 state 或者 prop 的更新

组件中的 stateprops 也可以测试验证。

it('sets loading state to true on save press', () => {
  const component = mount(<MyComponent />);
  component.find('[className="save-button"]').simulate('click');
  expect(component.state('isLoading')).toEqual(true);
  component.unmount();
});

与 document 的其他交互

因为,mount 可以渲染完整的 DOM,因此,可以包含其他很多方面的测试。

这也包括与当前 document 关联的 cookies。它可以通过 document.cookie 访问。为了防止测试之间相互影响,你或许需要用到以下方式

beforeEach(() => {
  document.cookie = '';
});

去重置它的值。

如果,组件在挂载时想同步 cookie 的值到 state 中,可以考虑以下的测试:

it('syncs state with user cookie on mount', () => {
  document.cookie = 'cookieOne=valueOne:::false&valueTwo:::true';
  
  const component = mount(<MyComponent />);
  expect(component.state('valueOne')).toEqual(false);
  expect(component.state('valueTwo')).toEqual(true);
  component.unmount();
});

最后的思考

希望这能说明在一起使用 Jest 和 Enzyme 是存在可能,这里的示例也具可读性,足以用到你自己的项目中。

另外一个提示:如果,用户交互没有产生预期的结果,Enzyme 中的 debug 可以帮到你

console.log(component.debug())

这时,需要去看看组件挂载后的快照。

多谢你们喜欢,如果,你有更多的见解,可以在评论中提出!😁

我还写了其他的文章: