2

I just joined a team where we use react, redux, recompose to construct components to build UI. There aren't any unit tests in the application and there isn't consistent architecture for the application. I decided to take it upon myself to add unit tests using jest and react-testing-library. I succeed with few snapshot tests but I am struggling with unit testing. I am still learning react and pretty new to redux. I would love some suggestion. I am going to share a component which renders a table with column and row. I would love a feedback.

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 Answer 1

3

I generally start out with a simple "should render without crashing" test. I prefer to export and test the undecorated component, in your case Contacts.

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

In the test file

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()}
      />
    );
  });
});

At this point I run a code coverage report to see how much I have, then add more tests with varying prop values and/or using the react-testing-library's matchers to target buttons or elements to assert text is visible or trigger callbacks, etc, until I have the coverage I want.

Sometimes some of your components may rely on context provider, and in this case RTL allows you to specify wrappers. For example if your component gets decorated with react-intl for string localization, you can provide a wrapper.

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

...

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

Create a wrapper

import { IntlProvider } from 'react-intl';

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

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

and to test, specify the wrapper in the render options argument

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

react-testing-library has a lot of documentation, but it is worth reading through. Hope this helps you get going.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.