3

I am trying to figure out how to test callbacks that are given as props to react functional components using jest and react testing library.

Example scenario: I am testing a component that renders a modal. When a user clicks the 'Close' button on the modal, the parent component hides the modal. So logic is this:

 const ParentComp = () => {
  const [openModal, setOpenModal] = useState(false);
  return (
   <>
     <MyModal showModal={openModal} onClose={() => setOpenModal(false)} />
     <button data-testid="open-modal-button" onClick={()=> setOpenModal(true)}>Test</button>
  </>
 }

const MyModal = ({showModal, onClose}) => {
  return (
   {showModal && <>
     <div>This is a modal</div>
     <button data-testid="close-modal-button" onClick={onClose}>Close</button>
  </>
  }
 }

I am mocking the modal in my tests for the parent component as I dont want to rely on the actual modal component. With react testing library, how do I trigger the onClose in my parent component so I can test the setOpenModal(false)?

jest.mock('../MyModal');
beforeEach(() => {
  MyModal.mockImplementation(() => <div data-testid="my-modal" />);
});

it('should close the modal' () => {
const { container, getByTestId } = render(
    <MyParentComp />
);
const openModalButton = getByTestId('open-modal-button');

fireEvent.click(openModalButton);
const myModal = getByTestId('my-modal');

expect(myModal).toBeDefined();

//How to test setOpenModal(false) on parent component?

});

1
  • I dont know if this is the proper way, but the mock implementation you create can have an onClick that fires the close event, and then you can fireEvent on it. Commented Nov 28, 2020 at 1:10

1 Answer 1

5

For your example, there is actually no need to mock the MyModal component unless the MyModal component has many external dependencies. See testing-recipes.html#mocking-modules.

In order to trigger the onClose function, you still need to trigger the onClick function on MyModal component.

Besides, You did not mock the MyModal component correctly. Here is a working example:

ParentComp.tsx:

import React, { useState } from 'react';
import { MyModal } from './MyModal';

export const ParentComp = () => {
  const [openModal, setOpenModal] = useState(false);
  return (
    <>
      <MyModal showModal={openModal} onClose={() => setOpenModal(false)} />
      <button data-testid="open-modal-button" onClick={() => setOpenModal(true)}>
        Test
      </button>
    </>
  );
};

MyModal.tsx:

import React from 'react';

export const MyModal = ({ showModal, onClose }) => {
  return (
    showModal && (
      <>
        <div>This is a modal</div>
        <button data-testid="close-modal-button" onClick={onClose}>
          Close
        </button>
      </>
    )
  );
};

ParentComp.test.tsx:

import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { ParentComp } from './ParentComp';

function MockedMyModal({ onClose, showModal }) {
  return (
    showModal && (
      <>
        <div>This is a mocked modal</div>
        <button data-testid="my-modal" onClick={onClose}>
          Close
        </button>
      </>
    )
  );
}

jest.mock('./MyModal', () => {
  return { MyModal: MockedMyModal };
});

describe('65038548', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should open the modal', () => {
    const { getByTestId } = render(<ParentComp></ParentComp>);
    const openModalButton = getByTestId('open-modal-button');

    fireEvent.click(openModalButton);
    const myModal = getByTestId('my-modal');

    expect(myModal).toBeDefined();
  });

  it('should close the modal', () => {
    const { getByTestId, queryByText } = render(<ParentComp></ParentComp>);
    const openModalButton = getByTestId('open-modal-button');
    fireEvent.click(openModalButton);

    const closeModalButton = getByTestId('my-modal');
    expect(closeModalButton).toBeDefined();

    fireEvent.click(closeModalButton);

    expect(queryByText('This is a mocked modal')).toBeNull();
  });
});

Why use queryByText instead of getByTestId, see Asserting elements are not present

unit test result:

 PASS  examples/65038548/ParentComp.test.tsx
  65038548
    ✓ should pass (34 ms)

----------------|---------|----------|---------|---------|-------------------
File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------------|---------|----------|---------|---------|-------------------
All files       |    87.5 |      100 |   66.67 |   85.71 |                   
 ParentComp.tsx |    87.5 |      100 |   66.67 |   85.71 | 8                 
----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.848 s

source code: https://github.com/mrdulin/jest-v26-codelab/tree/main/examples/65038548

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.