본문 바로가기
개발 관련 학습 및 문제해결

리액트 form , handleChange Mocking ,테스트 코드 짜기

by 날파리1 2022. 11. 30.

문제: form 테스트 중 handleChange 함수를 모킹해주라는 오류가 나타나는데 아샬님 강의에선 그러지 않았다? 그리고 handleChange는 어떻게 테스트 해주나?

 

오늘은 CRUD에서 C 에 해당하는 form 양식을 작성하고 테스트를 하고 있었다. formPage 에는 input 태그와 그것의 값들을 바꿔주는 handleChange 함수들을 넣어놓았는데 이러한 handleChange 함수와 value 값은 store로 넘겨주어 store에서 관리해주고 있었다.

그래서 handleChange에 변화하는 값을 넣고 Create 해당하는 함수가 동작하는지를 보려는데 handleChange를 모킹해주라고 한다.

왜..? 지난 아샬님 강의에선 그러지 않았는데..?

 

원인: 단순하다. import 해오는 객체를 모킹하고 페이지에서 그곳에서 정의된 함수를 사용중이라면 전부 모킹해주어야한다. 아니면 import에 해당하는 객체를 전혀 모킹하지 않으면 모킹하지 않은 원래의 import 해오는 객체를 사용한다.

리액트로 게시판을 만들던 중 아샬님께서 강의에서 postStore 와 postFormStore를 만들어 관리중 게시물을 만들어 저장하는 save 함수는 postStore에서 정의하고 formStore에서는 handleChange 함수들만 정의해주었다. 그때에는 왜 저걸 나누지라고 생각했었다. save는 formStore에 있어야하지 않나...? 라는 생각을 했었는데 아마도 이렇게 테스트를 간편하게해주려는 의도도 있었던 것 같다.

 

아래처럼 handleChange를 모킹해주지 않고 mockSave 만해주어도 통과가 된다.(postFormStore를 모킹하지 않아서) 

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

const mockSave = jest.fn();

jest.mock('../hooks/usePostSore', () => () => ({
  save: mockSave,
}));

test('PostForm', () => {
  render(<PostForm />);

  fireEvent.change(screen.getByLabelText('작성자'), {
    target: { value: '작성자' },
  });
  fireEvent.change(screen.getByLabelText('제목'), {
    target: { value: '제목' },
  });
  fireEvent.change(screen.getByLabelText('내용'), {
    target: { value: '내용' },
  });

  fireEvent.click(screen.getByText('등록'));

  expect(mockSave).toBeCalledWith({
    author: '작성자',
    title: '제목',
    body: '내용',
  });
});

handleChange 모킹해주기

나는 그렇지 않고 form에서 모든것을 다 정의해놓아서 전부 모킹해주어야한다.

아래처럼 handleChange에 해당하는 함수를 직접 정의해주고서야 통과가 된다. 휴 이마저도 한잠 씨름하고 알았다. 

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

const navigate = jest.fn();

const mockCreate = jest.fn();

const basicTemplateForm = {
  title: '',
  koreanSentence: '',
  englishSentence: '',
  description: '',
  youtubeUrl: '',
};

jest.mock('react-router-dom', () => ({
  useNavigate() {
    return navigate;
  },
}));

jest.mock('../../hooks/useBasicTemplatesAdminFormStore', () => () => ({
  createBasicTemplate: mockCreate,
  basicTemplateForm,
  changeTitle: (title) => {
    basicTemplateForm.title = title;
  },
  changeEnglishSentence: (englishSentence) => {
    basicTemplateForm.englishSentence = englishSentence;
  },
  changeKoreanSentence: (koreanSentence) => {
    basicTemplateForm.koreanSentence = koreanSentence;
  },
  changeDescription: (description) => {
    basicTemplateForm.description = description;
  },
}));

const context = describe;

describe('BasicTemplateFormPage', () => {
  context('renders BasicTemplateFormPage', () => {
    it('shows label texts', () => {
      render(<BasicTemplateFormPage />);

      screen.getByText('1분 완성 템플릿 작성하기');

      screen.getByLabelText('제목');

      screen.getByLabelText('영어문장');

      screen.getByLabelText('한글문장');

      screen.getByLabelText('상황설명');

      fireEvent.change(screen.getByLabelText('제목'), { target: { value: '인사하기' } });
      fireEvent.change(screen.getByLabelText('영어문장'), { target: { value: 'Hi!' } });
      fireEvent.change(screen.getByLabelText('한글문장'), { target: { value: '안녕!' } });
      fireEvent.change(screen.getByLabelText('상황설명'), { target: { value: '간단한 인사' } });

      fireEvent.click(screen.getByText('등록'));

      expect(mockCreate).toBeCalledWith({
        title: 'ㄹㄹ',
        koreanSentence: '',
        englishSentence: '',
        description: '',
        youtubeUrl: '',
      });
    });
  });

그래도 ...오늘도 프론트엔드 테스트코드에 한발짝 다가갔다!

댓글