본문 바로가기
개발/테스트코드

리액트 모달 컴포넌트 테스트

by 안뇽! 2023. 8. 27.
반응형

react test library를 통해 모달 컴포넌트 테스트를 작성하였다.

요구사항

버튼을 누르면 모달이 열린다.

모달 외부를 누르면 모달이 닫힌다.

모달을 클릭했을때는 모달이 닫히면 안된다.

테스트코드

import { render, screen } from '@testing-library/react';
import Test from '..';
import userEvent from '@testing-library/user-event';

test('default : modal is not visible', () => {
  render(<Test />);
  // 특정 텍스트를 통해 모달이 처음에는 안보이는 상태임을 확인한다.(=dom에 없는지)
  const modalText = 'This is a modal. Click outside to close.';
  expect(screen.queryByText(modalText)).not.toBeInTheDocument();
});

test('clicking button opens the modal', () => {
  render(<Test />);
  // 버튼을 눌러 모달을 연다.
  const openModalButton = screen.getByRole('button', { name: '모달 열기' });
  userEvent.click(openModalButton);
  // 특정 텍스트를 통해 모달이 열렸는지(=dom에 있는지) 본다.
  const modalText = 'This is a modal. Click outside to close.';
  expect(screen.getByText(modalText)).toBeInTheDocument();
});

test('clicking outside the modal closes it', () => {
  render(<Test />);
  // 일단 모달을 연다
  const openModalButton = screen.getByRole('button', { name: '모달 열기' });
  userEvent.click(openModalButton);
  // 모달 백드롭을 잡는다.
  const modalBackground = screen.getByTestId('modal-background');
  userEvent.click(modalBackground);
  // 모달이 닫혔는지(=dom에 없는지) 본다
  const modalText = 'This is a modal. Click outside to close.';
  expect(screen.queryByText(modalText)).not.toBeInTheDocument();
});

모달이 열렸는지 닫혔는지 여부

모달이 열렸는지 닫혔는지 여부는 toBeInTheDocument()를 이용했다.

 

모달 백드롭을 잡는 방법은 document.body가 아닌 data-testid를 이용했다.

<Backdrop data-testid='modal-background' onClick={() => setOpen(false)}>
   ...
</Backdrop>

document.body는 모달 자체도 포함하는데, 요구사항을 보았을 때 모달을 클릭했을때는 모달이 닫히면 안되기 때문이다.

 

다른 사람들은 모달 테스트 코드를 작성할 때 어떻게 작성하는지 궁금하다. data-testid 말고 다른 방법이 있을까.

클릭 테스트

fireEvent대신 userEvent를 사용했다.

fireEvent는 click이벤트만 dom을 통해 테스트하는 반면, userEvent는 클릭할때 일어나는 모든 이벤트를 테스트하기 때문이다.(사용자관점)

https://wnsdufdl.tistory.com/545

 

fireEvent와 userEvent 차이

fireEvent와 userEvent 차이 fireEvent와 userEvent는 둘다 react test 에서 사용자 동작을 시물레이션하기 위해 사용되는 테스트 도구이다. fireEvent DOM 이벤트를 직접 트리거하는 저수준 도구이다. 컴퓨터 입

wnsdufdl.tistory.com


실제 컴포넌트 코드

// Page
import { useState } from 'react';
import Modal from './Modal';

const Test = () => {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>모달 열기</button>
      <Modal open={open} setOpen={setOpen} />
    </>
  );
};

export default Test;

 

// Modal.tsx
import React from 'react';
import styled from 'styled-components';

const Modal = ({
  open,
  setOpen,
}: {
  open: boolean;
  setOpen: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
  if (!open) return <></>;
  return (
    <Backdrop data-testid='modal-background' onClick={() => setOpen(false)}>
      {
        <div data-testid='modal' onClick={(e) => e.stopPropagation()}>
          <Span>This is a modal. Click outside to close.</Span>
        </div>
      }
    </Backdrop>
  );
};

export default Modal;

const Backdrop = styled.div`
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background: rgba(255, 255, 255, 0.8);

  display: flex;
  align-items: center;
  justify-content: center;
`;

const Span = styled.span`
  padding: 30px;
  border: 1px solid skyblue;
  border-radius: 30px;
`;
반응형