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 元件庫)版本相符的 Adapter。例如,當您使用 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

透過 JSDOM 使用 Enzyme

透過 React Native 使用 Enzyme

透過 Jest 使用 Enzyme

透過 Lab 使用 Enzyme

透過 Tape 和 AVA 使用 Enzyme

基本用法

淺層繪製

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()包含ReactTestUtils.act()的api來進行包裝,因此您不需要手動進行包裝。

透過.act()和斷言來觸發處理常式,常見模式是

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

我們無法在Enzyme中以.act()包裝.prop()(或.props())的結果,因為這會中斷傳回值的相等性。不過,您可以使用.invoke()來簡化程式碼

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

未來

Enzyme的未來

貢獻

參閱貢獻指南

實際運用

使用enzyme的組織和專案可以將自己列出在此

授權

MIT 授權