프론트엔드 테스트 코드 입문

anvel·2025년 5월 17일

항해 플러스

목록 보기
25/39

항해 플러스 프론트엔드 - 프론트엔트 테스트 코드

프론트엔드 테스트 코드

이번주차 과제는 드디어 고대하던 테스트 코드의 작성이었습니다. 프론트엔드의 단위 테스트와 통합 테스트 영역이 어떻게 작성되어야 하는 지 테스트 코드의 기능들과 친해지는 과정을 경험하였습니다.


단위 테스트(Unit Test)

코드의 가장 작은 단위(함수, 메서드, 컴포넌트 등)를 독립적으로 테스트하는 방식입니다.

  • 특정 함수나 로직이 의도대로 작동하는지 검증
  • 외부 의존성 없이, 순수하게 입력 → 출력의 관계만 확인
  • 빠르고 간단하며 테스트 대상 코드만 집중적으로 검증

describe('getWeekDates', () => {
  it('주중의 날짜(수요일)에 대해 올바른 주의 날짜들을 반환한다', () => {
    // 이번 주의 수요일
    const thisWednesday = getThisWeeksDay(WEEKDAY_INDEX.WEDNESDAY);
    const weekDates = getWeekDates(thisWednesday);
    expect(weekDates.length).toBe(7);

    // 이번 주의 수요일로 함수를 호출했을 때의 각 요일별 날짜들
    Object.values(WEEKDAY_INDEX).forEach((target) => {
      const expectedDate = getThisWeeksDay(target);
      expect(weekDates[target].toISOString().split('T')[0]).toBe(
        expectedDate.toISOString().split('T')[0]
      );
    });
  });

  it('주의 시작(월요일)에 대해 올바른 주의 날짜들을 반환한다', () => {
    // 이번 주의 시작 일요일? 월요일?
    const thisSunday = getThisWeeksDay(WEEKDAY_INDEX.SUNDAY);
    const weekDates = getWeekDates(thisSunday);
    expect(weekDates.length).toBe(7);

    // 이번 주의 월요일로 함수를 호출했을 때의 각 요일별 날짜들
    Object.values(WEEKDAY_INDEX).forEach((target) => {
      const expectedDate = getThisWeeksDay(target);
      expect(weekDates[target].toISOString().split('T')[0]).toBe(
        expectedDate.toISOString().split('T')[0]
      );
    });
  });
});

통합 테스트(Integration Test)

여러 모듈, 컴포넌트, 시스템 간 상호작용을 테스트하는 방식입니다.

  • 개별 단위들이 서로 연동될 때 문제가 없는지 검증
  • 상대적으로 복잡하고 느리며 데이터베이스, 네트워크, 외부 API 등 의존성을 포함
  • 오류 발생 시 원인 추적이 비교적 어려움
const renderApp = () =>
  render(
    <ChakraProvider>
      <Providers>
        <App />
      </Providers>
    </ChakraProvider>
  );

describe('EventManageForm', () => {
  let user: ReturnType<typeof userEvent.setup>;

  beforeEach(() => {
    vi.setSystemTime(new Date('2025-05-01'));
    user = userEvent.setup();
  });

  it('제목, 날짜, 시작/종료 시간이 없을 때 추가 버튼 클릭 시 필수 정보 에러가 나타난다', async () => {
    renderApp();

    const button = await screen.findByTestId('event-submit-button');
    await user.click(button);

    expect(await screen.findByText('필수 정보를 모두 입력해주세요.')).toBeInTheDocument();
  });

  it('시작 시간이 종료 시간보다 늦을 경우 시간 설정 에러가 나타난다', async () => {
    renderApp();

    await user.type(screen.getByLabelText('제목'), '회의');
    await user.type(screen.getByLabelText('날짜'), '2025-05-01');
    await user.clear(screen.getByLabelText('시작 시간'));
    await user.type(screen.getByLabelText('시작 시간'), '12:00');
    await user.clear(screen.getByLabelText('종료 시간'));
    await user.type(screen.getByLabelText('종료 시간'), '11:00');

    await user.click(await screen.findByTestId('event-submit-button'));

    expect(await screen.findByText(/시간 설정을 확인해주세요/)).toBeInTheDocument();
  });

  it('반복 체크 시 반복 입력 필드가 나타난다', async () => {
    renderApp();

    const checkbox = screen.getByLabelText('반복 일정') as HTMLInputElement;
    if (!checkbox.checked) {
      await user.click(checkbox);
    }

    expect(screen.getByLabelText('반복 유형')).toBeInTheDocument();
    expect(screen.getByLabelText('반복 간격')).toBeInTheDocument();
    expect(screen.getByLabelText('반복 종료일')).toBeInTheDocument();
  });

  it('반복 체크 해제 시 반복 입력 필드가 사라진다', async () => {
    renderApp();

    const checkbox = screen.getByLabelText('반복 일정') as HTMLInputElement;
    if (checkbox.checked) {
      await user.click(checkbox);
    }

    expect(screen.queryByLabelText('반복 유형')).not.toBeInTheDocument();
  });
});

이번 과제를 통해 겪은 경험

이번 과제는 과정 중 특히나 기대되던 부분이었습니다. 테스트 코드의 목적을 기준으로 테스트 코드를 작성하고 해당 기능을 검증하려면 어떤 값을 확인하면 되는 지 에 대한 공부가 되었습니다.

단위 테스트의 경우에는 학습 이전에 어느정도 예측이 되는 부분이 있었습니다. 함수의 입력값과 출력값 자체만 두고 검증하는 방식이라고 생각했었는데, 시간과 관련된 부분이 개입이 될 수 있다는 점에서는 외부 요인도 고려가 되어야 하는구나 하는 점을 알게 되었습니다.

통합 테스트의 경우에는 처음 작성해보는 부분이 많아, 사용자의 행동이 화면에 반영되기를 기다리는 부분에서 조금 애를 먹었습니다. 화면을 조작하는 사용자의 행동과, 이후에 Mock API를 통해 비동기적으로 랜더링 되길 기다린 후에 검증이 가능한 부분들이 실제 현업에 적용할때도 고민을 많이 해야겠구나 하였습니다. 다행인 점은 백엔드 개발도 하고 있다보니 MSW로 가상의 API를 세팅하는 것을 어느정도 익숙한 느낌이 들었다는 점이었습니다.


만족하고 지속할 부분

사실 이번주에 처음으로 금요일 새벽까지 과제를 하는 경험을 하였습니다. 다른 과제들은 그래도 일요일엔 기본과제가 끝나고, 심화과제를 주중에 천천히 진행하는 순서였는데, 이번 과제는 그냥 양 자체가 많아서 목요일 밤이 되서야 심화과제가 끝을 보였습니다.
다음 과제도 포기하기 않고 과제를 끝까지 하는 노력하려고 합니다.

시도할 부분

글쓰기에 익숙해지고자 2일에 한번 글을 쓰는 습관 들이기는 이번주는 어려운 편이었습니다. 개발 시간이 여유가 나야 가능한 점과 내용에 이해가 충분히 이뤄지고 해당 배경을 설명할 수 있어햐 한다는 점이 발목을 잡았습니다.
변명이긴 하지만 이번주로 과제에 집중해야할 시간이 많이 예상되는 경우라면 과제에 더 집중하고, 글을 쓰는 것은 간단하게 세 꼭지만 작성하는 루틴으로 진행할 예정입니다.

마치며

테스트 코드와 관련된 챕터가 2주 진행이 되는데, 이 과정이 현재 실제 업무에 차용하여 적용할 부분을 찾을 수 있을 지 걱정이 됩니다. 수만줄에 달하는 레거시 JS 코드에 대하여 리팩토링을 먼저 진행하게 될텐데, 리팩토링의 과정 중에 테스트 코드로 자동화 검증을 만들어 함수가 정확한 In Out을 유지할 수 있는 지 검증하는 방향으로 개발을 진행할 것 같습니다.
이전 과제들 보단 경험이 적어 막히는 부분이 있지만, 그래도 시간을 오래 들여서라도 확실하게 경험해보려고 합니다. 이번 경험이 좋은 기회가 되었으면 좋겠습니다.

0개의 댓글