React + Storybook에서 테스트 코드 작성해 보기
이번에 React + Storybook에서 간단히 테스트 코드를 작성하는 방법에 대해서 알아보았습니다.
준비
먼저 React 프로젝트를 생성 후 필요한 라이브러리를 설치합니다.
npm create vite@latest [project-name] -- --template react-ts npx storybook init --builder @storybook/builder-vite npm install -D @storybook/test
*이번 테스트 코드 작성에서는 vitest를 기준으로 작성해 보고 싶어 참고 자료와 달리 @storybook/test를 설치해 테스트 코드를 작성하고 있습니다.
Storybook 작성
이번 Storybook 작성에서 React 관련 코드는 스킵 하도록 하겠습니다.
preview.tsx
import type { Preview } from '@storybook/react' import React from 'react' import '../src/index.css' const preview: Preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, layout: 'fullscreen', controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, }, decorators: [ (Story) => { return <Story /> }, ], } export default preview
Page.stories.tsx
import type { Meta, StoryObj } from '@storybook/react' import { within, expect, userEvent } from '@storybook/test' import { Page } from './Page' const meta: Meta<typeof Page> = { component: Page, } export default meta type Story = StoryObj<typeof Page> export const Default: Story = { render: (_args) => <Page />, play: async ({ canvasElement }) => { const canvas = within(canvasElement) const user = userEvent.setup() const email = canvas.getByTestId('email') const password = canvas.getByTestId('password') await user.type(email, 'email@example.com') await user.type(password, 'password') expect(canvas.getByTestId('email')).toHaveValue('email@example.com') expect(canvas.getByTestId('password')).toHaveValue('password') }, }
작성 후 Storybook에서 확인해 보면 아래의 이미지와 같이 테스트 코드의 움직임이 구현 됩니다.
보충
조금더 추가해 계정을 추가하는 기능을 만든다고 가정해 보고 API 와 통합된 테스트 코드도 작성해 보겠습니다.
*API는 실제 API가 아닌 Msw를 이용해 작성해 보았습니다.
Msw 설치
추가로 아래의 라이브러리를 설치합니다.
npm install -D msw npm install -D msw-storybook-addon
Storybook 작성
위와 같이 React 와 Msw 관련 코드는 스킵 후 빠르게 Storybook 관련 코드만 작성해 보겠습니다.
preview.tsx
import type { Preview } from '@storybook/react' import React from 'react' import '../src/index.css' import { initialize, mswLoader } from 'msw-storybook-addon' // // Start Mock Service Worker initialize({ onUnhandledRequest: 'bypass' }) const preview: Preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, layout: 'fullscreen', controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, }, loaders: [mswLoader], decorators: [ (Story) => { return <Story /> }, ], } export default preview
Page.stories.tsx
... export const Default: Story = { render: (_args) => <Page />, parameters: { msw: [buildAddUser()], }, play: async ({ canvasElement }) => { const canvas = within(canvasElement) const user = userEvent.setup() const email = canvas.getByTestId('email') const password = canvas.getByTestId('password') await user.type(email, 'email@example.com') await user.type(password, 'password') expect(canvas.getByTestId('email')).toHaveValue('email@example.com') expect(canvas.getByTestId('password')).toHaveValue('password') const submit = canvas.getByTestId('submit') await user.click(submit) // 로딩 표시 await expect(await canvas.findByText('...loading')).toBeInTheDocument() // 성공 상태 확인 await expect(await canvas.findByText('success')).toBeInTheDocument() }, }
작성 후 Storybook에서 다시 확인해 보면 아래와 같이 테스트 코드의 움직임을 확인해 볼 수 있습니다.
이외에도 Msw를 이용하면 error 나 loading 상태의 mock을 쉽게 만들 수 있으므로 추가 테스트 코드를 작성해 보는 것도 좋을 것 같습니다.