이번에 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을 쉽게 만들 수 있으므로 추가 테스트 코드를 작성해 보는 것도 좋을 것 같습니다.