Storybookのloaderが期待通りに動作しなかった(2025/2/21時点)
はじめに
Storybook の loaders
機能を利用して、React Testing Library でストーリーを流用しようと試みましたが、期待通りに動作しなかったため、その原因について調べました。
環境
以下のバージョンを使用しています。
"storybook": "8.5.3"
"@testing-library/react": "16.2.0",
"msw": "^2.7.0",
"msw-storybook-addon": "^2.0.4",
なにを実現しようとしたか
以下のようなPageコンポーネントに対してテストを書こうとしていました。
このコンポーネントは、最上位層のpage.tsx
でデータ取得処理を行い、その結果をprops
として受け取ることを想定しています。
export type Props = React.ComponentProps<typeof Posts>
export const Page = ({ ...props }: Props) => {
return (
<section aria-label="記事一覧">
<HeaderWithLink title="Posts" />
<Posts posts={props.posts} />
</section>
)
}
mswとStorybook を組み合わせて、データ取得処理を含むストーリーを作成しました。具体的には、parameters.msw.handlers
で API をモック化し、loaders
で非同期データをフェッチしてコンポーネントに渡す形です。
import * as Home from '.'
const meta: Meta<typeof Home.Page> = {
title: 'pages/Home',
component: Home.Page,
parameters: {
msw: {
handlers: [handler()],
},
},
loaders: [
async () => {
const posts = await fetch("hogehoge")
const props = { posts } satisfies Home.Props
return { props }
},
],
}
export default meta
type Story = StoryObj<typeof Home.Page>
export const Default: Story = {
render: (_, { loaded }) => {
return <Home.Page {...loaded.props} />
},
}
Storybook 上では問題なく動作し、モックされたデータが Default
ストーリーに反映され、正しく描画されることを確認できました。
次に、このストーリーを React Testing Library で流用し、テストに利用しようとしました。以下のように実装しました
import { composeStories } from '@storybook/react'
import * as Stories from './index.stories'
import { setupMockServer } from '@/mocks/server';
...
setupMockServer(handler())
const { Default } = composeStories(Stories)
describe('Page Component', () => {
test('主要コンテンツの表示', async () => {
render(<Default />)
await waitFor(() => {
expect(
screen.getByRole('region', { name: '記事一覧' }),
).toBeInTheDocument()
})
})
})
しかし<Default >
は特にデータを持っておらず、正しく描画されませんでした。
原因
Storybook の loaders
機能は、Jest 環境では公式にサポートされていませんでした。そのため、composeStories
を使用しても context.loaded
が undefined
となり、非同期データがコンポーネントに渡されない状態となってしまうようでした。
これから解決されるらしい
最近、以下のStorybook のPRにてloaders
の Jest 環境でのサポートが追加されてました(反映はまだ)。
この PR で、composeStories
で生成されたストーリーに新しいメソッド load
が追加されおり、このメソッドを使用することで、loaders
の結果を Jest 環境でも反映できるようになっているようです。
PRの内容によると以下のように、load
メソッドを明示的に呼び出すことで、非同期データをテストに利用できるみたいです。
test('主要コンテンツの表示', async () => {
await Default.load(); // loaders を実行
render(<Default />); // コンポーネントをレンダリング
await waitFor(() => {
expect(
screen.getByRole('region', { name: '記事一覧' }),
).toBeInTheDocument();
});
});
まとめ
- Storybook の
loaders
機能は、現状Jest 環境で正しく動作しない。 - 最近の PR により、
load
メソッドが追加されていたので近い未来にこの問題が解決されるっぽい。
参考
Mock Service WorkerAPI mocking library for browser and Node.jsmswjs.io