미니 프로젝트를 진행하는데 Styled-Components
를 써서 css를 적용하는데
ThemeProvider
를 통해 공통 스타일 속성을 사용해 일관성 있게 컴포넌트를 스타일링하고자 했다.
export default function App({ Component, pageProps }: AppProps) {
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<RecoilRoot>
<ThemeProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
</RecoilRoot>
<ReactQueryDevtools initialIsOpen={false} />
</Hydrate>
</QueryClientProvider>
);
}
위 코드의 ThemeProvider
는 다크모드 적용때문에 한번 더 감싸준 것이고 결국 Styled-Components의 테마이다.
jest
와 react-testing-library
로 컴포넌트 이벤트 테스트를 하던 도중 theme
context를 사용하고 있던 부분 때문에 다음과 같은 오류가 났다.
● button을 클릭하면 click 이벤트가 동작한다.
TypeError: Cannot read properties of undefined (reading 'regular')
3 | const Text = styled.div`
4 | font-style: normal;
> 5 | font-weight: ${({ theme }) => theme.fontWeight.regular};
| ^
6 | `;
7 |
8 | export const TextBodyTitle = styled(Text)`
import { render } from '@testing-library/react';
render(<SomeComponentWithStyledComponentTheme/>);
테스트환경에서는 Styled-Component ThemeProvider가 없기 때문에 theme
context가 undefined
였던 것이다.
storybook
에서도 독립적인 컴포넌트 하나하나에 대한 styled-components 적용때문에
데코레이터를 사용해 ThemeProvider
를 감싸줬던 것을 기억하며 jest 환경에서도 이를 적용해줘야 함을 알게 되었다
jest에서 styled-component 사용하기 위해 의존성 설치
yarn add -D jest-styled-components
jest config의 setupFilesAfterEnv 설정에 jest-styled-components 추가해 각 독립적인 테스트에 해당 라이브러리가 적용될 수 있게 해준다.
setupFilesAfterEnv: ['...yours', 'jest-styled-components'],
ThemeProvider Wrapper를 적용한 custom jest render 함수를 만들어 사용할 수 있게 한다.
import 'jest-styled-components';
import { ThemeProvider } from 'styled-components';
import { render } from '@testing-library/react';
import themeMode from '../styles/theme';
const Wrapper = ({ children }) => <ThemeProvider theme={themeMode['light']}>{children}</ThemeProvider>;
const renderWithStyledComponent = (ui, options) => render(ui, { wrapper: Wrapper, ...options });
export * from '@testing-library/react';
export { renderWithStyledComponent as render };
어디에 선언하던 import해서 사용할 수 있겠지만 test환경에서만 사용하기 때문에
__test__
폴더에 넣어주었다.
styled-component theme가 적용된 컴포넌트를 테스트할 때 이런 식으로 import하면 된다.
// 커스텀 render 위치
import { render } from '../utils';
// 원래 rtl의 render 위치
import { fireEvent } from '@testing-library/react';
테스트하기
import { render } from '../utils';
import Button from '@/components/atoms/Button';
import { COLOR } from '@/styles/constants';
import { fireEvent } from '@testing-library/react';
/** 이 테스트를 통과하면 Styled-Components ThemeProvider가 jest 환경에 잘 적용된 것입니다. */
it('primary theme 버튼은 primary color를 가지고 있다.', () => {
const { getByRole } = render(
<Button colorTheme="primary" onClick={jest.fn()}>
hello
</Button>,
);
expect(getByRole('button')).toHaveStyleRule('background-color', COLOR.brand1);
});
it('button을 클릭하면 click 이벤트가 동작한다.', () => {
const mockOnClick = jest.fn();
const { getByRole } = render(
<Button colorTheme="primary" onClick={mockOnClick}>
hello
</Button>,
);
fireEvent.click(getByRole('button'));
expect(mockOnClick).toHaveBeenCalled();
});
특히 이렇게 작은 컴포넌트는 어차피 UI나 사용자 이벤트 대부분은 스토리북으로 테스트하지만
그래도 Styled-components theme가 잘 적용된다는 것을 보여줄 수 있는 테스트인 것 같다
버튼 컴포넌트의 Styled 컴포넌트에는 다음과 같은 background-color
속성이 있었다.
background-color: ${(props) =>
props.colorTheme === 'primary' ? props.theme.brandColor : buttonTheme[props.colorTheme][0]};
assertion 부분의 toHaveStyleRule
검증은 jest-styled-components
의 검증 메소드다.
toHaveStyle
이 실제 컴포넌트에 해당 css 속성이 존재하는지를 검증한다면
toHaveStyleRule
은 해당 컴포넌트에 해당 css 규칙이 존재하는지를 검증한다고 하는데
명확한 차이는 사용해봐야 좀 더 알 것 같다.
위에서 내려온 스타일인지 해당 컴포넌트에 명시되어있는 스타일인지에 대한 차이인 것 같긴 하다
jest에서는 최대한 외부적인 것과 독립적으로 컴포넌트 그 자체의 상호작용에 대해 테스트하고 싶은데
아무리 개별적인 컴포넌트더라 하더라도 styled-components가 전부 적용되어있을테니 저 커스텀 render 함수만 사용하게 될 것 같다
이런식으로 결국 전역상태관리도구나 api 상태관리 도구 provider들도 계속 들어가지 않을까 싶다..