Storybook の「play 関数+ composeStories」を使用したテストで、MSW のハンドラを通らないケースの原因について
今回は Storybook に play 関数を定義して API コールをしている Story があり、その Story を composeStories 関数で再利用したテストで、時折テストが失敗する不安定なテストの原因とその対策についてです。
- Storybook v7(2024-06-05 時点で最新のメジャーバージョンは v8)
- MSW v1(2024-06-05 時点で最新のメジャーバージョンは v2)
概要
Story 例)
// Foo.stories.tsx
const Uploading: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup({ delay: null });
await user.upload(
canvas.getByTestId("upload"),
new File["dummy"](),
"sample.jpg",
{ type: "image/jpg" }
);
},
parameters: {
msw: {
handlers: [
rest.post("https://example.com/files", (_, res, ctx) => {
return res(ctx.delay("infinite"), ctx.status(200));
}),
],
},
},
};
テスト例)
// Foo.test.tsx
import * as stories from "./Foo.stories";
const { Uploading } = composeStories(stories);
test("ファイルアップロード中はボタンがdisabledであること", async () => {
server.use(...Uploading.parameters["msw"].handlers);
const { container } = render(<Uploading />);
await Uploading.play(container);
expect(screen.getByRole("button", { name: "アップロード" })).toBeDisabled();
});
composeStories を使用して Story を再利用するテストでは、MSW ハンドラの登録はされないので、忘れずにテストコード上で MSW の setupServer 関数を使用して作成した server.use()
を使用しているにも関わらず、時折 MSW の resolver を通らずにテストが失敗するといった状態です。
現象は時折発生していて、resolver を通る時もあれば通らない時もあるといった不安定な状態。
原因
// Foo.stories.tsx
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup({ delay: null }); // ここの `{ delay: null }`
await user.upload(canvas.getByTestId("upload"), new File["dummy"], "sample.jpg", { type: "image/jpg" });
},
Testing Library(Storybook の userEvent が内部的に使用)の userEvent.type
は多量の文字数入力のエミュレートで実行時間が伸びるといった問題があり、その対策は以下の方法があります。
対策方法
userEvent.type
をuserEvent.paste
に変えるuserEvent
をfireEvent
に変えるuserEvent.setup
の引数に{ delay: null }
を追加する
今回のケースでは「3. userEvent.setup
の引数に { delay: null }
を追加する」を採用していました。
その結果、ユーザ操作のエミュレート時に Testing Library がよしなに wait していた処理をスキップする状態となっており、テスト実行時の状況によっては MSW の resolver を通ったり通らなかったりといった状態になっていたようです。
対策
引数のオブジェクトから delay をトル
// Foo.stories.tsx
const Uploading: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- const user = userEvent.setup({ delay: null });
+ const user = userEvent.setup();
await user.upload(canvas.getByTestId("upload"), new File["dummy"], "sample.jpg", { type: "image/jpg" });
},
parameters: {
msw: {
handlers: [
rest.post("https://example.com/files", (_, res, ctx) => {
return res(ctx.delay("infinite"), ctx.status(200));
})
]
}
}
}
さいごに
時折失敗するテストは原因を特定するのが難しく、特定までに時間が掛かるのがつらいところです。
似たような不安定なテストを抱えている方は、userEvent.setup
の引数に { delay: null }
が指定されていないか確認してみてください。
この記事を書いた人
- 2013年にアーティスに入社。システムエンジニアとしてアーティスCMSを使用したWebサイトや受託システムの構築・保守に携わる。環境構築が好き。
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー