Enzyme

Join the chat at https://gitter.im/enzymejs/enzyme

npm Version License Build Status Coverage Status

Enzyme 是一个用于 React 的 JavaScript 测试工具,它使测试 React 组件的输出变得更容易。您还可以操作、遍历,并在某些情况下模拟给定输出的运行时。

Enzyme 的 API 旨在通过模仿 jQuery 的 DOM 操作和遍历 API 来实现直观和灵活。

从 Enzyme 2.x 或 React < 16 升级

您是否要检查 Enzyme 是否与 React 16 兼容?您目前是否正在使用 Enzyme 2.x?太好了!查看我们的 迁移指南,以获取有关迁移到支持 React 16 的 Enzyme v3 的帮助。

安装

要开始使用 enzyme,您可以通过 npm 安装它。您需要安装 enzyme 以及与您使用的 react(或其他 UI 组件库)版本相对应的适配器。例如,如果您将 enzyme 与 React 16 一起使用,则可以运行

npm i --save-dev enzyme enzyme-adapter-react-16

每个适配器可能还有一些额外的对等依赖项,您也需要安装它们。例如,enzyme-adapter-react-16reactreact-dom 有对等依赖项。

目前,Enzyme 有适配器可以提供与 React 16.xReact 15.xReact 0.14.xReact 0.13.x 的兼容性。

以下适配器由 enzyme 官方提供,并具有以下与 React 的兼容性

Enzyme 适配器包 React semver 兼容性
enzyme-adapter-react-16 ^16.4.0-0
enzyme-adapter-react-16.3 ~16.3.0-0
enzyme-adapter-react-16.2 ~16.2
enzyme-adapter-react-16.1 `~16.0.0-0 \ \ ~16.1`
enzyme-adapter-react-15 ^15.5.0
enzyme-adapter-react-15.4 15.0.0-0 - 15.4.x
enzyme-adapter-react-14 ^0.14.0
enzyme-adapter-react-13 ^0.13.0

最后,您需要配置 enzyme 以使用您希望它使用的适配器。为此,您可以使用顶层的 configure(...) API。

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });

第三方适配器

社区可以创建额外的(非官方)适配器,使 enzyme 可以与其他库一起使用。如果您创建了一个适配器,并且它没有包含在下面的列表中,请随时向此 README 发起 PR 并添加指向它的链接!已知的第三方适配器是

适配器包 用于库 状态
enzyme-adapter-preact-pure preact (稳定)
enzyme-adapter-inferno inferno (开发中)

运行 Enzyme 测试

Enzyme 对您使用的测试运行器或断言库没有偏好,并且应该与所有主要的测试运行器和断言库兼容。Enzyme 的文档和示例使用 mochachai,但您应该能够将其推断到您选择的框架。

如果您有兴趣将 Enzyme 与自定义断言和便利函数一起使用来测试您的 React 组件,您可以考虑使用

将 Enzyme 与 Mocha 一起使用

将 Enzyme 与 Karma 一起使用

将 Enzyme 与 Browserify 一起使用

将 Enzyme 与 SystemJS 一起使用

将 Enzyme 与 Webpack 一起使用

将 Enzyme 与 JSDOM 一起使用

将 Enzyme 与 React Native 一起使用

将 Enzyme 与 Jest 一起使用

将 Enzyme 与 Lab 一起使用

将 Enzyme 与 Tape 和 AVA 一起使用

基本用法

浅渲染

import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import sinon from 'sinon';

import MyComponent from './MyComponent';
import Foo from './Foo';

describe('<MyComponent />', () => {
  it('renders three <Foo /> components', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.find(Foo)).to.have.lengthOf(3);
  });

  it('renders an `.icon-star`', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.find('.icon-star')).to.have.lengthOf(1);
  });

  it('renders children when passed in', () => {
    const wrapper = shallow((
      <MyComponent>
        <div className="unique" />
      </MyComponent>
    ));
    expect(wrapper.contains(<div className="unique" />)).to.equal(true);
  });

  it('simulates click events', () => {
    const onButtonClick = sinon.spy();
    const wrapper = shallow(<Foo onButtonClick={onButtonClick} />);
    wrapper.find('button').simulate('click');
    expect(onButtonClick).to.have.property('callCount', 1);
  });
});

阅读完整的 API 文档

完整 DOM 渲染

import React from 'react';
import sinon from 'sinon';
import { expect } from 'chai';
import { mount } from 'enzyme';

import Foo from './Foo';

describe('<Foo />', () => {
  it('allows us to set props', () => {
    const wrapper = mount(<Foo bar="baz" />);
    expect(wrapper.props().bar).to.equal('baz');
    wrapper.setProps({ bar: 'foo' });
    expect(wrapper.props().bar).to.equal('foo');
  });

  it('simulates click events', () => {
    const onButtonClick = sinon.spy();
    const wrapper = mount((
      <Foo onButtonClick={onButtonClick} />
    ));
    wrapper.find('button').simulate('click');
    expect(onButtonClick).to.have.property('callCount', 1);
  });

  it('calls componentDidMount', () => {
    sinon.spy(Foo.prototype, 'componentDidMount');
    const wrapper = mount(<Foo />);
    expect(Foo.prototype.componentDidMount).to.have.property('callCount', 1);
    Foo.prototype.componentDidMount.restore();
  });
});

阅读完整的 API 文档

静态渲染的标记

import React from 'react';
import { expect } from 'chai';
import { render } from 'enzyme';

import Foo from './Foo';

describe('<Foo />', () => {
  it('renders three `.foo-bar`s', () => {
    const wrapper = render(<Foo />);
    expect(wrapper.find('.foo-bar')).to.have.lengthOf(3);
  });

  it('renders the title', () => {
    const wrapper = render(<Foo title="unique" />);
    expect(wrapper.text()).to.contain('unique');
  });
});

阅读完整的 API 文档

React Hooks 支持

Enzyme 支持 react hooks,在 .shallow() 中存在一些限制,这是由于 React 的浅渲染器中的上游问题。

  • useEffect()useLayoutEffect() 在 React 浅渲染器中不会被调用。 相关问题

  • useCallback() 不会在 React 浅渲染器中记忆回调。 相关问题

ReactTestUtils.act() 包裹

如果您使用的是 React 16.8+ 和 .mount(),Enzyme 会将包括 .simulate().setProps().setContext().invoke() 在内的 API 与 ReactTestUtils.act() 进行包裹,因此您无需手动进行包裹。

使用 .act() 触发处理程序并进行断言的常见模式是

const wrapper = mount(<SomeComponent />);
act(() => wrapper.prop('handler')());
wrapper.update();
expect(/* ... */);

我们无法在 Enzyme 内部将 .prop()(或 .props())的结果与 .act() 进行包裹,因为这会破坏返回值的相等性。但是,您可以使用 .invoke() 来简化代码

const wrapper = mount(<SomeComponent />);
wrapper.invoke('handler')();
expect(/* ... */);

未来

Enzyme 未来

贡献

请参阅 贡献者指南

实际应用

使用 enzyme 的组织和项目可以将自己列在 这里

许可证

MIT