使用 test 和 react-testing-library 测试反应组件

Test a react component using test and react-testing-library

我刚刚加入了一个团队,我们使用 react、redux、recompose 构建组件来构建 UI。应用程序中没有任何单元测试,应用程序也没有一致的架构。我决定自己使用 jest 和 react-testing-library 添加单元测试。我成功地完成了很少的快照测试,但我在单元测试中苦苦挣扎。我仍在学习反应并且对 redux 还很陌生。我会喜欢一些建议。我将共享一个组件,该组件呈现带有列和行的 table。我希望得到反馈。

import React, { useEffect, useState } from 'react';
import { compose } from 'recompose';
import { connect } from 'react-redux';

import { clearAll, fetchContacts } from '~/store/resources/contacts/actions';
import { isDevEnv } from '~/utils';

import Sidebar from './Sidebar';
import Table from './Table';
import Toolbar from './Toolbar';

const Contacts = ({ clearAll, fetchContacts, ...props }) => {
  const [searchValue, setSearchValue] = useState('');
  const [isSidebarOpen, setIsSidebarOpen] = useState(false);
  const [canonicalFormValues, setCanonicalFormValues] = useState({ active: true });

  useEffect(() => {
    fetchContacts();
    return () => {
      clearAll();
    };
  }, []);

  const closeSidebar = () => {
    if (isDevEnv) {
      console.log('hit close function');
    }
    setIsSidebarOpen(false);
  };

  return (
    <div>
      <Toolbar
        searchValue={searchValue}
        setSearchValue={setSearchValue}
        setIsSidebarOpen={setIsSidebarOpen}
      />
      <Table setCanonicalFormValues={setCanonicalFormValues} />
      <Sidebar
        isSidebarOpen={isSidebarOpen}
        closeSidebar={closeSidebar}
        canonicalFormValues={canonicalFormValues}
      />
      {isDevEnv && (
        <div>
          This is coming from the contact folder
          <br />
          state values:
          <br />
          {JSON.stringify({ searchValue })}
          <br />
          {JSON.stringify({ isSidebarOpen })}
          <br />
          {JSON.stringify({ canonicalFormValues })}
        </div>
      )}
    </div>
  );
};

const mapDispatchToProps = {
  clearAll,
  fetchContacts,
};

export default compose(
  connect(
    null,
    mapDispatchToProps,
  ),
)(Contacts);

我通常从一个简单的 "should render without crashing" 测试开始。我更喜欢导出和测试未修饰的组件,在你的例子中 Contacts.

export const Contacts = ({ clearAll, fetchContacts, ...props }) => { ...

在测试文件中

import React from 'react';
import { render } from '@testing-library/react';
import { Contacts } from '.';

// mock the other imported components, they should already be tested alone, right?
jest.mock('./Sidebar');
jest.mock('./Table');
jest.mock('./Toolbar');

describe('Contacts', () => {
  it('should render without crashing', () = {
    render(
      <Contacts
        // pass all the props necessary for a basic render
        clearAll={jest.fn()}
        fetchContacts={jest.fn()}
      />
    );
  });
});

此时我 运行 一个代码覆盖率报告来查看我有多少,然后添加更多具有不同属性值的测试 and/or 使用 react-testing-library 的匹配器来定位按钮或断言文本可见的元素或触发回调等,直到我得到我想要的覆盖范围。

有时您的某些组件可能依赖上下文提供程序,在这种情况下,RTL 允许您指定包装器。例如,如果您的组件装饰有 react-intl 以进行字符串本地化,您可以提供一个包装器。

export const Contacts = ({ clearAll, fetchContacts, intl }) => { ...

...

export default compose(
  connect(
    null,
    mapDispatchToProps,
  ),
  injectIntl,
)(Contacts);

创建包装器

import { IntlProvider } from 'react-intl';

const IntlWrapper = ({ children }) => (
  <IntlProvider locale="en">{children}</IntlProvider>
);

const intlMock = {
  ...
  formatMessage: message => message,
  ...
};

要进行测试,请在渲染选项参数中指定包装器

render(
  <Contacts
    // pass all the props necessary for a basic render
    clearAll={jest.fn()}
    fetchContacts={jest.fn()}
    intl={intlMock}
  />,
  {
    wrapper: IntlWrapper
  }
);

react-testing-library 有很多文档,但值得一读。希望这能帮助你开始。