0

I have to test the below component :

import React from 'react';
import { connect } from 'react-redux';

import { open as openModal } from 'redux/actions/ModalActions';
import { MODAL_CONTENT_ADD_TO_ALBUM } from 'constants/ModalNameConstants';

import ContentAddToAlbumModal from 'components/ContentAddToAlbumModal';
import AddSection from 'components/AddSection';

export function AddToAlbumButton(props) {
  const { openUploadModal } = props;

  return (
    <>
      <div>
        <div>
          <AddSection
            onClick={openUploadModal}
          />
        </div>
      </div>
      <ContentAddToAlbumModal/>
    </>
  );
}

function mapDispatchToProps(dispatch, props) {
  return {
    openUploadModal() {
      return dispatch(
        openModal(MODAL_CONTENT_ADD_TO_ALBUM, props.content.get('id'))
      );
    },
  };
}

export default connect(null, mapDispatchToProps)(AddToAlbumButton);

I have written my test case as below :

import React from 'react';
import {render} from '@testing-library/react';
import {AddToAlbumButton} from 'components/AddToAlbumButton';

jest.mock('components/ContentAddToAlbumModal', () => {
  return function ContentAddToAlbumModal() {
      return (
      <div>
          ContentAddToAlbumModal
      </div>
      )
  };
});

jest.mock('components/AddSection', () => {
  return function AddSection({openUploadModal}) {
      return (
      <div onClick={openUploadModal}>
          AddSection
      </div>
      )
  };
});


describe('AddToAlbumButton component', () => {

  const props = {
    openUploadModal: jest.fn()
  };

  it('Should render snapshot of AddToAlbumButton component correctly', () => {
    const {asFragment} = render(<AddToAlbumButton {...props} />);    
    expect(asFragment()).toMatchSnapshot();
  })
});

I want to test my mapDispatchToProps function too, how could I test it without exporting it publicly. And how should I test the connected component ?

I was looking over the internet for suggestions. One I found is to mock the 'react-redux' module by creating a file in mocks folder of jest directory with the code snippet as:

const mockDispatch = jest.fn(action => action);
module.exports = {
    connect: (mapStateToProps, mapDispatchToProps) => reactComponent => ({
        mapStateToProps,
        mapDispatchToProps: (dispatch = mockDispatch, ownProps) => (
            mapDispatchToProps(dispatch, ownProps)
        ),
        [reactComponent.displayName || reactComponent.name || 'reactComponent']: reactComponent,
        mockDispatch,
    }),
    Provider: ({ children }) => children,
};

How should I use this above code snippet with 'React Testing Library'. What's missing from my test case ? How should I proceed ?

Thanks

user3760959
  • 457
  • 1
  • 6
  • 18

1 Answers1

0

You can use redux-mock-store package to create a mocked store. Wrap your component with Provider with this mocked store.

You can get all the actions by calling store.getActions(). Finally, you can use any assertion library to test the payload.

This testing method is closer to integration testing, integrating React component, connect, and mapDispatchToProps function.

index.tsx:

import React from 'react';
import { connect } from 'react-redux';
import { open as openModal } from './actions';
import ContentAddToAlbumModal from './components/ContentAddToAlbumModal';
import AddSection from './components/AddSection';
const MODAL_CONTENT_ADD_TO_ALBUM = 'MODAL_CONTENT_ADD_TO_ALBUM';

export function AddToAlbumButton(props) {
  return (
    <>
      <AddSection onClick={props.openUploadModal} />
      <ContentAddToAlbumModal />
    </>
  );
}

function mapDispatchToProps(dispatch, props) {
  return {
    openUploadModal() {
      return dispatch(openModal(MODAL_CONTENT_ADD_TO_ALBUM, props.content.get('id')));
    },
  };
}

export default connect(null, mapDispatchToProps)(AddToAlbumButton);

components/ContentAddToAlbumModal.tsx:

import React from 'react';

export default function ContentAddToAlbumModal() {
  return <div>real ContentAddToAlbumModal</div>;
}

components/AddSection.tsx:

import React from 'react';

export default function AddSection({ onClick }) {
  return <div onClick={onClick}>real AddSection</div>;
}

actions.ts:

export function open(type, id) {
  return { type, payload: { id } };
}

index.test.tsx:

import { render, fireEvent, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import createMockStore from 'redux-mock-store';
import AddToAlbumButton from './';

const mockStore = createMockStore();

jest.mock('./components/ContentAddToAlbumModal', () => {
  return function ContentAddToAlbumModal() {
    return <div>ContentAddToAlbumModal</div>;
  };
});

jest.mock('./components/AddSection', () => {
  return function AddSection({ onClick }) {
    return <div onClick={onClick}>AddSection</div>;
  };
});

describe('AddToAlbumButton', () => {
  test('should pass', () => {
    const store = mockStore({});
    const content = {
      get(key) {
        return '123';
      },
    };
    render(
      <Provider store={store}>
        <AddToAlbumButton content={content} />
      </Provider>
    );
    fireEvent.click(screen.getByText(/AddSection/));
    expect(store.getActions()).toEqual([{ type: 'MODAL_CONTENT_ADD_TO_ALBUM', payload: { id: '123' } }]);
  });
});

test result:

 PASS  examples/69525117/index.test.tsx (8.636 s)
  AddToAlbumButton
    ✓ should pass (35 ms)

------------|---------|----------|---------|---------|-------------------
File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------|---------|----------|---------|---------|-------------------
All files   |     100 |      100 |     100 |     100 |                   
 actions.ts |     100 |      100 |     100 |     100 |                   
 index.tsx  |     100 |      100 |     100 |     100 |                   
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.788 s, estimated 10 s
Lin Du
  • 88,126
  • 95
  • 281
  • 483