使用测试和反应测试库测试反应组件

时间:2020-01-29 03:13:37

标签: javascript reactjs redux jestjs react-testing-library

我刚刚加入一个团队,在这里我们使用react,redux,recompose来构建用于构建UI的组件。应用程序中没有任何单元测试,并且该应用程序没有一致的体系结构。我决定自己使用jest和react-testing-library添加单元测试。我通过少量快照测试获得了成功,但是我在单元测试方面苦苦挣扎。我仍在学习对Redux的反应和新手。我希望有一些建议。我将共享一个组件,该组件呈现具有列和行的表。我希望得到反馈。

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);

1 个答案:

答案 0 :(得分:0)

我通常从一个简单的“应该呈现而不会崩溃”测试开始。对于您的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()}
      />
    );
  });
});

这时,我运行一个代码覆盖率报告以查看我有多少,然后添加更多具有不同prop值的测试,和/或使用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 options参数中指定包装器

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

react-testing-library有很多文档,但是值得通读。希望这对您有所帮助。