[react] redux 적용한 프로젝트에 Testing-library/react 사용하기

Ell!·2021년 10월 31일
2

react

목록 보기
4/28

테스트케이스 도입 결정

스크림도르 프로젝트를 진행한지 어언 4개월. 그동안 막무가내로(?) 진행해왔던 프로젝트를 뒤돌아보니 맘에 안드는 점이 너무나도 많아보였다. 이젠 리팩토링에 대한 생각이 조금씩 들게되었다. 리팩토링을 진행하기에 앞서서 testcase가 반드시 필요하다는 생각에 JestTesting-library/react를 사용해서 유저 관점의 테스팅을 도입하기로 결정했다.

문제 발생 : redux

개별 컴포넌트에서는 문제없이 테스트케이스가 작성되었지만, page 컴포넌트들에서 문제가 발생했다. testing이 돌아가면서

cannot read property 0000 of 'undefined'

에러가 발생한 것.

0000가 redux에서 관리하는 모달창이었기에 redux때문에 발생한 문제라는 것은 쉽게 알아차릴 수 있었다. 해결책 역시 redux-toolkit 홈페이지에서 쉽게 찾을 수 있었다.

// utils/test.js

import React from 'react';
import {render as rtlRender} from '@testing-library/react';
import {configureStore} from '@reduxjs/toolkit';
import modalReducer from 'store/modules/modal';
import { Provider } from 'react-redux';

function render(
  ui,
  {
    preloadedState,
    store = configureStore({
      reducer: { modal: modalReducer },
      preloadedState,
    }),
    ...renderOptions
  } = {},
) {
  function Wrapper({ children }) {
    return (
      <Provider store={store}>
            {children}
      </Provider>
    );
  }
  return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}

export * from '@testing-library/react';
export { render }; // render에다가 redux store, themeprovider 넣어서 override

해결 방법 해설

// 공식문서 render API

async function render<ComponentType>(
  component: Type<ComponentType>,
  renderOptions?: RenderComponentOptions<ComponentType>,
): Promise<RenderResult<ComponentType, ComponentType>>
async function render<DirectiveType, WrapperType = WrapperComponent>(
  component: Type<DirectiveType>,
  renderOptions?: RenderDirectiveOptions<DirectiveType, WrapperType>,
): Promise<RenderResult<DirectiveType, WrapperType>>

RTLrender 함수는 첫번째 인자로 컴포넌트를 받고, 두번째 인자로, renderOptions를 받는다. 위의 함수를 보면, 우리가 임의로 만든 render함수에 인자로 우리가 만들어둔 redux의 reducer를 제공해서 store를 만들고, wrapper를 만들어서 기존 RTLrender함수에 덮어씌워서 return해주고 있다.

// test.js

import React from 'react';
import { render } from 'utils/test';
import { toBeInTheDocument } from '@testing-library/jest-dom';
import NotFound from './index';

describe('<Not Found>', () => {
  it('renders 404', () => {
    const { getByText } = render(<NotFound />);
    const _404 = getByText('404 Not Found');
    expect(_404).toBeInTheDocument();
  });
});

test.js 파일에서는 이처럼 render를 우리가 overrider한 utils에서 불러오면 된다.

추가 사항

redux를 해결하니 styled-components, Router 등에서도 문제가 발생했다. 역시 같은 방법으로 해결해주었다.

import React from 'react';
import { render as rtlRender } from '@testing-library/react';
import { configureStore } from '@reduxjs/toolkit';
import modalReducer from 'store/modules/modal';
import {
  StylesProvider,
  ThemeProvider as MuiThemeProvider,
} from '@material-ui/styles';
import { ThemeProvider as StyledThemeProvider } from 'styled-components';
import { Provider } from 'react-redux';
import theme from 'styles/MaterialTheme';
import { BrowserRouter as Router } from 'react-router-dom';

function render(
  ui,
  {
    preloadedState,
    store = configureStore({
      reducer: { modal: modalReducer },
      preloadedState,
    }),
    ...renderOptions
  } = {},
) {
  function Wrapper({ children }) {
    return (
      <Provider store={store}>
        <StylesProvider injectFirst>
          <StyledThemeProvider theme={theme}>
            <MuiThemeProvider theme={theme}>
              <Router>{children}</Router>
            </MuiThemeProvider>
          </StyledThemeProvider>
        </StylesProvider>
      </Provider>
    );
  }
  return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}

export * from '@testing-library/react';
export { render }; // render에다가 redux store, themeprovider 넣어서 override

참고

https://www.daleseo.com/react-testing-library/

https://redux.js.org/usage/writing-tests

https://github.com/boostcamp-2020/Project15-C-Client-Based-Formula-Editor/wiki/jest----@react-testing-library-%EB%A1%9C-Redux-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0

profile
더 나은 서비스를 고민하는 프론트엔드 개발자.

0개의 댓글