Skip to content

[6팀 김수현] Chapter 3-1. 프론트엔드 테스트 코드#20

Open
suhyeon57 wants to merge 8 commits intohanghae-plus:mediumfrom
suhyeon57:medium
Open

[6팀 김수현] Chapter 3-1. 프론트엔드 테스트 코드#20
suhyeon57 wants to merge 8 commits intohanghae-plus:mediumfrom
suhyeon57:medium

Conversation

@suhyeon57
Copy link

@suhyeon57 suhyeon57 commented Aug 17, 2025

Medium

7주차 과제 체크포인트

기본과제

Medium

  • 총 11개의 파일, 115개의 단위 테스트를 무사히 작성하고 통과시킨다.

질문

Q. medium.useEventOperations.spec.tsx > 아래 toastFn과 mock과 이 fn은 무엇을 해줄까요?

mocking을 사용하는 이유
--> 실제 DB나 외부 환경에 영향을 주지 않고, 독립적이고 빠른 테스트를 하기 위함입니다.

vi.fn()은 호출 여부와 인자를 추적할 수 있는 mock 함수를 만들 때 사용합니다.

vi.mock('notistack', …)은 notistack 모듈을 테스트 환경에서 가짜 버전으로 바꿔치기하는데,
이렇게 하면 useSnackbar가 반환하는 enqueueSnackbar가 실제 스낵바를 띄우는 대신,
우리가 만든 mock 함수(toastFn)를 실행하게 됩니다.

따라서, 테스트에서는 expect(toastFn).toHaveBeenCalledWith('일정 삭제 실패', { variant: 'error' });처럼
UI 동작 대신 호출 자체와 전달된 메시지만 검증할 수 있습니다.

따라서, 테스트에서 expect(enqueueSnackbarFn).toHaveBeenCalledWith('일정 삭제 실패', { variant: 'error' }); 같은 검증을 할 수 있다.

Q. medium.integration.spec.tsx > 여기서 ChakraProvider로 묶어주는 동작은 의미있을까요? 있다면 어떤 의미일까요?

네, 의미가 있습니다. 테스트는 실제 동작하는 화면과 최대한 유사해야 정확해집니다.

ChakraProvider는 Chakra UI의 테마와 context를 제공하기 때문에, 이를 테스트에서 함께 적용하면 실제 UI 환경과 동일한 조건에서 컴포넌트를 검증할 수 있습니다.

Q. handlersUtils > 아래 여러가지 use 함수는 어떤 역할을 할까요? 어떻게 사용될 수 있을까요?

Q. setupTests.ts > 왜 이 시간을 설정해주는 걸까요?

심화 과제

  • App 컴포넌트 적절한 단위의 컴포넌트, 훅, 유틸 함수로 분리했는가?
  • 해당 모듈들에 대한 적절한 테스트를 5개 이상 작성했는가?

과제 셀프회고

테스트 코드 작성 후 통과하면 짜릿하다는 기분을 느낄 수 있다고 했는데 정말 느꼈습니다.. ai를 사용하지 않고 함수들이 어디에서 어떻게 쓰이는지 직접 확인해 가면서 테코를 작성하니 시간이 많이 걸렸지만, 재미를 느꼈던 것 같습니다.

실무에서는 테코를 작성하지 않아서 항상 코드를 수정, 추가하려고 할 때 다른 곳에서 에러가 나지 않을까 생각하며 조심스럽게 수정해나가는데 테코를 작성하면 마음 놓고 수정할 수 있지 있겠다라고 생각했고, 시간이 된다면 실무 테코도 작성해보고 싶습니다.

기술적 성장

renderHook 사용 경험

처음에 커스텀 훅을 테스트하려고 했을 때, 훅을 직접 호출했더니 오류가 발생했습니다.
검색해보니, 테스트 환경에서는 훅을 직접 호출할 수 없고, react-testing-library의 renderHook을 사용해야 한다는 것을 알게 되었습니다.

renderHook은 훅을 호출하고, 훅이 반환하는 현재 값과 메서드를 result.current를 통해 접근할 수 있도록 해줍니다.
이를 통해 훅의 상태와 메서드를 안전하게 테스트하며, 원하는 동작을 검증할 수 있었습니다.

act ()의 사용

테스트 코드에서 훅을 검증하던 중 navigate('prev')를 호출했을 때, 상태가 업데이트되지 않는 문제가 있었습니다.

const { result } = renderHook(() => useCalendarView());

result.current.navigate('prev'); 

const date1 = result.current.currentDate;
const date2 = new Date('2025-09-01T00:00:00.000Z'); 
assertDate(date1, date2);

처음에는 단순히 result.current.navigate('prev')만 호출하면 currentDate가 즉시 바뀔 거라고 생각했습니다. 하지만 실제로는 리액트 상태 업데이트가 비동기적이며, Testing Library는 모든 상태 변경을 act()로 감싸야 안전하게 반영된다는 규칙이 있었습니다.

그래서 아래처럼 act()로 감싸주자 정상적으로 동작했습니다.

const { result } = renderHook(() => useCalendarView());
  act(() => {
    result.current.navigate('prev');
  });
  const date1 = result.current.currentDate;
  const date2 = new Date('2025-09-01T00:00:00.000Z');
  assertDate(date1, date2);

위 내용처럼 리액트 테스트 환경에서는 모든 상태 변경이 act 내부에서 실행되어야 한다는 걸 알았습니다.

코드 품질

//********************다시 작성 필요******************/
it('정의된 이벤트 정보를 기준으로 적절하게 저장이 된다', async () => {
  const initialEvents: Event[] = [
    {
      id: '1',
      title: '기존 회의',
      date: '2025-07-01',
      startTime: '09:00',
      endTime: '10:00',
      description: '기존 팀 미팅',
      location: '회의실 B',
      category: '업무',
      repeat: { type: 'none', interval: 0 },
      notificationTime: 10,
    },
  ];
  setupMockHandlerCreation(initialEvents);
  const { result } = renderHook(() => useEventOperations(false));
  const newEvent: Event = {
    id: '',
    title: '신규 세미나',
    date: '2025-07-10',
    startTime: '11:00',
    endTime: '12:00',
    description: '신규 프로젝트 세미나',
    location: '회의실 A',
    category: '업무',
    repeat: { type: 'none', interval: 0 },
    notificationTime: 10,
  };

  await act(async () => {
    await result.current.saveEvent(newEvent);
  });

  await waitFor(() => {
    expect(result.current.events).toEqual([initialEvents[0], { ...newEvent, id: '2' }]);
  });
});

이 테스트에 대한 이해가 맞는지 의문이 들어서 다시 리팩토링 해보고 싶습니다.

학습 효과 분석

과제 피드백

과제를 통해 테스트 코드에 대한 기본적인 이해가 많이 생겼습니다.
사전 스터디 때는 테스트 코드의 이론적인 부분만 접했기 때문에 깊게 다루지 못했지만, 이번 과제를 통해 직접 작성하고 실행해보면서 테스트가 실제로 어떻게 동작하는지를 명확하게 경험할 수 있어 좋았습니다.

리뷰 받고 싶은 내용

import { fetchHolidays } from '../../apis/fetchHolidays';
describe('fetchHolidays', () => {
  it('주어진 3월의 공휴일 삼일절을 반환한다', () => {
    expect(fetchHolidays(new Date('2025-03-01'))).toEqual({
      '2025-03-01': '삼일절',
    });
  });
  it('공휴일이 없는 7월에 대해 빈 객체를 반환한다', () => {
    expect(fetchHolidays(new Date('2025-07-01'))).toEqual({});
  });

  it('여러 공휴일이 있는 10월에 대해 모든 공휴일을 반환한다', () => {
    expect(fetchHolidays(new Date('2025-10-05'))).toEqual({
      '2025-10-03': '개천절',
      '2025-10-05': '추석',
      '2025-10-06': '추석',
      '2025-10-07': '추석',
      '2025-10-09': '한글날',
    });
  });
});

이 테코를 진행할 때 만약 휴일이 추가될 경우 테스트 코드 에러가 날텐데 그런 경우를 대비해서 작성할 수 있는 방법이 있을까요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant