testing-library/react에서 form 테스트 안 된 이유

2025. 12. 16. 18:50Frontend

이전 글, Shadcn Dialog 컴포넌트에서 onSubmit 안 된 이유

 

1. 문제 상황

Shadcn Dialog 제출 테스트가 계속 실패했다.

개발 모드에서 제출되었지만,
테스트 코드에서 onSubmit 함수가 호출되지 않아 계속 실패했다.

 

2. 문제 확인

<Button> 컴포넌트를 눌러 제출 여부를 확인하는 테스트 코드에서 
실제 DOM 환경과 테스트 환경 차이로 외부 form과 연계되지 않는다고 의심했다.

form 내부에서 onSubmit 함수가 동작하는지 확인하기 위해
<Dialog> 컴포넌트 코드를 지워봤다.

Shadcn Dialog 컴포넌트를 사용자가 열면
DOM에서 form과 button이 분리된다.
<body>
  <div>
    <form
      id="email-reservation"
      role="form"
      onSubmit={submitFn}
    >
      <Button
        form="email-reservation"
        type="submit"
        onClick={clickFn}
      >
        예약하기
      </Button>
    </form>
  </div>
</body>

어디까지 실행되는지 알기 위해 이벤트 함수를 할당했다.

테스트하면 <Button> 클릭까지 진행되었고
onSubmit 이벤트는 동작하지 않았다.

이벤트 전파가 되지 않아서 <Button> 컴포넌트가 이벤트 전파를 차단하는 것으로 판단했다.

일반 <button> 태그로 바꿔서도 진행해 봤지만,
onSubmit 이벤트가 호출되지 않는 건 동일했다.

 

3. 해결 방안 탐색

테스트 환경에서 이벤트 자체를 실행하도록
userEvent 매서드 대신 fireEvent를 사용했다.

test("예약하기를 누르면 사용자 정의 함수가 호출된다.", async () => {
  const props = {
    submitFn: jest.fn(async (e) => { e.preventDefault(); }),
    buttonValue: "창 열기",
    reservedDate: "2025.10.14. 화요일",
    reservedTime: "13:30:00",
  };
  render(<EmailReservationDialog {...props} />);

  const submitButton = screen.getByRole("button", { name: "예약하기" });
  // userEvent.click(submitButton); // 수정 전
  fireEvent.click(submitButton); // 수정 후

  await waitFor(() => {
    expect(props.submitFn).toHaveBeenCalled();
  });
});

정상적으로 onSubmit 이벤트가 호출되었다. 
<Button> 컴포넌트로 바꿔도 정상적으로 호출되었다.

<Button> 컴포넌트가 이벤트 전파를 막은 것이 아닌, 
userEvent 매서드가 이벤트를 전파하지 않았다. 

 

4. 해결 방안 적용

다시 처음 테스트 컴포넌트 구조로 되돌렸다. 
<Dialog>를 누르면 <form> 태그와 <Button> 컴포넌트가 분리되는 구조에서 다시 테스트했다.

<Dialog>
  <form role="form" onSubmit={submitFn} id="email-reservation">
    {/* 다이얼로그 확장 버튼 */}
    <DialogTrigger asChild>
      <Button variant="outline">{buttonValue}</Button>
    </DialogTrigger>

    {/* 다이얼로그 내용 */}
    <DialogContent className="sm:max-w-[425px]">
      ...
      {/* 다이얼로그 버튼 */}
      <DialogFooter>
        <DialogClose asChild>
          <Button variant="outline">닫기</Button>
        </DialogClose>
        <Button type="submit" form="email-reservation">
          예약하기
        </Button>
      </DialogFooter>
    </DialogContent>
  </form>
</Dialog>

테스트 결과, 성공적으로 onSubmit 이벤트가 호출되었다.

제출 버튼이 form 외부에 있어서 연계되지 않는 테스트 환경 문제가 아니라
테스트 라이브러리에서 이벤트 전파가 되지 않는 매서드(userEvent)를 사용한 것이 원인이었다.

testing-library 문서에 따르면,

Consider fireEvent.click, which creates a click event and dispatches that event on the given DOM node. This works properly for most situations when you simply want to test what happens when your element is clicked, ...

https://testing-library.com/docs/guide-events

 

FireEvent는 사용자 반응을 이벤트뿐만 아니라, 그에 맞는 요소 이벤트도 발생시켰다.

클릭 반응 같은 단순한 사용자 테스트가 아니면,
userEvent보다 fireEvent를 사용하는 것이 상호작용을 테스트하는 면에서 적합했다.