스크림도르 프로젝트를 진행한지 어언 4개월. 그동안 막무가내로(?) 진행해왔던 프로젝트를 뒤돌아보니 맘에 안드는 점이 너무나도 많아보였다. 이젠 리팩토링에 대한 생각이 조금씩 들게되었다. 리팩토링을 진행하기에 앞서서 testcase가 반드시 필요하다는 생각에 Jest
와 Testing-library/react
를 사용해서 유저 관점의 테스팅을 도입하기로 결정했다.
개별 컴포넌트에서는 문제없이 테스트케이스가 작성되었지만, 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>>
RTL
의 render
함수는 첫번째 인자로 컴포넌트를 받고, 두번째 인자로, renderOptions를 받는다. 위의 함수를 보면, 우리가 임의로 만든 render
함수에 인자로 우리가 만들어둔 redux의 reducer를 제공해서 store를 만들고, wrapper
를 만들어서 기존 RTL
의 render
함수에 덮어씌워서 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/