从外部组件反应测试库触发回调

Trigger callback from external component react testing library

我正在尝试弄清楚如何测试作为 props 给出的回调,以使用 jest 和 React 测试库对功能组件做出反应。

示例场景:我正在测试呈现模式的组件。当用户单击模态框上的 'Close' 按钮时,父组件会隐藏模态框。所以逻辑是这样的:

 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>
  </>
  }
 }

我在父组件的测试中模拟模态,因为我不想依赖实际的模态组件。使用 React 测试库,如何在我的父组件中触发 onClose 以便我可以测试 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?

});

对于您的示例,实际上不需要模拟 MyModal 组件,除非 MyModal 组件有很多外部依赖项。参见 testing-recipes.html#mocking-modules

为了触发onClose功能,您还需要在MyModal组件上触发onClick功能。

此外,您没有正确模拟 MyModal 组件。这是一个工作示例:

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

为什么使用 queryByText 而不是 getByTestId,参见

单元测试结果:

 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

源代码:https://github.com/mrdulin/jest-v26-codelab/tree/main/examples/65038548