본문 바로가기

메가테라 FE 생존코스 12기

[Debug] usestore에 mock 데이터 사용하기

개요

  • usestore-ts를 사용하여 상태관리를 해주고 있다.
  • 테스트 코드에서 상태값을 변경하여 화면에 변경된 값이 나타나는지 확인하려고 한다.
  • 처음에 describe 외부에서 상태값과 usestore mock을 생성해주었더니 컴포넌트에서 해당 상태값이 undefined로 나오는 문제가 있었다.
    • 이를 해결하기 위해 usestore mock을 describe 내부로 옮겨주었다.
    • 과연 이것이 해결방법인가? jest.mock에 대해서 먼저 알아보자.

그런데, 테스트 코드에서 상태값은 변경하여도 화면에는 변경된 상태값이 나타나지 않는 문제가 발생했다.

코드

import { render, screen } from '@testing-library/react';
import Orders from '.';
import fixture from '../../../fixtures';
import { OrdersType } from '../../types/ordersType';

const context = describe;

describe('Orders 컴포넌트', () => {
  const cart:OrdersType = {
    menu: [],
    totalPrice: 0,
  };
  jest.mock('../../hooks/useCartStore', () => () => [cart]);


  context('orders의 menu가 빈배열이 아니면', () => {
    beforeEach(() => {
      cart.menu = [fixture.food];
      cart.totalPrice = fixture.food.price;
    });

    it('menu 리스트를 렌더링한다.', () => {
      render(<Orders />);
      screen.getByText(/짜장면/);
    });
  });
});

접근

  1. jest.mock을 사용하는 방법이 올바른가?

jest 공식 홈페이지에서 jest.mock 을 사용하는 방법을 확인해보니, test 코드 외부에서 선언을 하는 것을 자주 볼 수 있었다.

그러므로 describe 내부가 아닌 외부에 mock을 선언하는 것이 옳다.

  1. mock으로 전달한 cart를 왜 컴포넌트에서 undefined로 인식하는가?

로컬서버에서 테스트 할 때에는 정상동작하고 undefined오류가 발생하지 않는 것으로 보아 테스트 코드에서 전달해주는 mock 데이터가 문제이다.

useStore(CartStore)의 반환값은 [NonFunctionProperties<Store>, Readonly<Store>]이다. 그러므로 mock 데이터에 전달해줄 때에도 객체로 전달해야한다.

const cart:OrdersType = {
  menu: [],
  totalPrice: 0,
};

jest.mock('../../hooks/useCartStore', () => () => [{ cart }]);

완성된 코드

import { render, screen } from '@testing-library/react';
import Orders from '.';
import fixture from '../../../fixtures';
import { OrdersType } from '../../types/ordersType';

const cart:OrdersType = {
  menu: [],
  totalPrice: 0,
};

jest.mock('../../hooks/useCartStore', () => () => [{ cart }]);

const context = describe;
describe('Orders 컴포넌트', () => {
  context('orders의 menu가 빈배열 이라면', () => {
    beforeEach(() => {
      render(<Orders />);
    });

    it('주문내역0개, 총 결제 예상 금액 0원, 주문하기 버튼, 취소하기 버튼을 렌더링합니다.', () => {
      expect(screen.getByText(/주문내역/)).toBeInTheDocument();
      expect(screen.getByText(/총 결제 예상 금액 0원/)).toBeInTheDocument();
    });
  });

  context('orders의 menu가 빈배열이 아니면', () => {
    beforeEach(() => {
      cart.menu = [fixture.food];
      cart.totalPrice = fixture.food.price;
    });

    it('menu 리스트를 렌더링한다.', () => {
      render(<Orders />);
      screen.getByText(/짜장면/);
      screen.getByText(/총 결제 예상 금액 8,000원/);
    });
  });
});