styled-components 로 구현한 컴포넌트 테스트하기

컴포넌트의 style 작성에는 여러가지 선택지가 있습니다.

그 중 저는 styled-components 를 가장 선호합니다.

이번 포스팅에서는 styled-components 를 사용한 컴포넌트를 테스트하기 위한 jest 환경을 설정해보겠습니다.


설정없이 jest 를 실행하면? throw Error!

styled-components<ThemeProvider /> 하위에서 사용할 수 있습니다.

만약 추가 설정없이 jest 를 실행하게 되면, render() 호출에서 에러가 발생합니다.

에러 발생 예시
import MyComponent from './MyComponent';
import {
    render,
} from '@testing-library/react';
 
describe('MyComponent 테스트', () => {
    it('DOM 에 렌더링 된다.', () => {
        // render() 호출 시, Error 발생
        render(
            <div data-testid="my-component">
                <MyComponent />
            </div>
        );
 
        const $myComponent = screen.getByTestId('my-component');
 
        expect($myComponent).toBeInTheDocument();
    });
});
에러 메시지
TypeError: Cannot read properties of undefined (reading 'MarkdownAnchor')

아래와 같이 render() 호출부에 <ThemeProvider /> 를 함께 넘겨주면 테스트가 정상적으로 실행되는 것을 확인할 수 있습니다.

테스트 정상 동작
import MyComponent from './MyComponent';
import {
    render,
} from '@testing-library/react';
 
describe('MyComponent 테스트', () => {
    it('DOM 에 렌더링 된다.', () => {
        // render() 성공
        render(
            <div data-testid="my-component">
                <ThemeProvier theme={theme}>
                    <MyComponent />
                </ThemeProvier>
            </div>
        );
 
        const $myComponent = screen.getByTestId('my-component');
 
        // 테스트 통과
        expect($myComponent).toBeInTheDocument();
    });
});

util 또는 hook 으로 <ThemeProvider /> 제공하기

테스트 코드를 작성할 때, 각 테스트 케이스별로 중복되는 코드들이 생기게 됩니다.

이러한 부분들을 공통 함수로 묶어내거나 추상화하지 않는 이유는, 테스트 코드를 읽어나가는 것으로 어떤 테스트를 수행하는지 파악할 수 있는 것이 더 테스트 코드의 가치를 높이기 때문입니다.


하지만 <ThemeProvider /> 를 감싸는 코드는 styled-components 설정을 테스트하는 것이 아닌 이상, 특정 컴포넌트를 테스트하는데 의미를 두기 어렵습니다.

그러므로 이 부분은 별도의 util 함수 또는 custom hook 으로 분리하여 사용해도 무방해 보입니다.


저는 @testing-library 에서 제공하는 render() 함수 처럼 util 함수 로 만드는 것이 좀 더 일관되는 패턴으로 생각하여 아래와 같이 작성해 보았습니다.

@/utils/testing-library/renderTestComponent.tsx
// react
import { 
    ReactElement,
} from 'react';
// jest
import { 
    render,
} from '@testing-library/react';
// styled-components
import { 
    ThemeProvider,
} from 'styled-components';
import theme from '@/styles/theme';
 
const renderTestComponent = (element: ReactElement) => {
    const { rerender } = render(
        <ThemeProvider theme={theme('light')}>
            {element}
        </ThemeProvider>
    );
 
    return {
        rerender,
    };
};
 
export default renderTestComponent;

이를 테스트 코드에 적용하면 다음과 같습니다.

util 함수를 적용한 테스트 코드
import MyComponent from './MyComponent';
// import {
//     render,
// } from '@testing-library/react';
import renderTestComponent from '@/utils/testing-library/renderTestComponent';
 
describe('MyComponent 테스트', () => {
    it('DOM 에 렌더링 된다.', () => {
        // render() 성공
        renderTestComponent(
            <div data-testid="my-component">
                <MyComponent />
            </div>
        );
 
        const $myComponent = screen.getByTestId('my-component');
 
        // 테스트 통과
        expect($myComponent).toBeInTheDocument();
    });
});

마치며

유닛 테스트를 하나씩 추가하며, coverage 가 높아져 가는 것이 하나의 재미요소가 되었습니다.

renderTestComponent() 처럼 테스트 환경을 위한 기능을 만드는 과정은 성취감과 연결되었습니다.

이 블로그 프로젝트는 개발 초기 시점인 만큼, 모든 컴포넌트를 테스트할 수 있도록 목표를 잡아야겠습니다!