はじめに
フロントエンド開発においてStorybookを導入しているが、ユーザー操作によるコンポーネントの動作を機械的にテストできていない課題があった。
Storybookを使って、コンポーネントの動作をテストできないか調べると@storybook/test-runner
が良さげだった。
この記事は、@storybook/test-runner
を使ったインタラクションテストの実装方法の備忘録。
参考記事
Storybook: https://storybook.js.org/
@storybook/test-runner: https://storybook.js.org/addons/@storybook/test-runner/
インタラクションテスト: https://storybook.js.org/tutorials/intro-to-storybook/react/ja/test/
サンプルコード
今回は曜日を選択するとメッセージが表示されるVueコンポーネントをサンプルに@storybook/test-runner
でインタラクションテストを書く。
- サンプル(曜日を選択するとメッセージが表示されるVueコンポーネント)
<template>
<div>
<select v-model="value">
<option value="" disabled>選択してください</option>
<option value="Mon">月曜日</option>
<option value="Tue">火曜日</option>
<option value="Wed">水曜日</option>
<option value="Thu">木曜日</option>
<option value="Fri">金曜日</option>
<option value="Sat">土曜日</option>
<option value="Sun">日曜日</option>
</select>
<div class="message">
<div v-if="value === 'Mon'">Monday</div>
<div v-if="value === 'Tue'">Tuesday</div>
<div v-if="value === 'Wed'">Wednesday</div>
<div v-if="value === 'Thu'">Thursday</div>
<div v-if="value === 'Fri'">Friday</div>
<div v-if="['Sat', 'Sun'].includes(value)" class="message_holiday">
土日だワッショイ!!
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('');
</script>
<style lang="scss" scoped>
.message {
margin-top: 16px;
&_holiday {
color: red;
}
}
</style>
土日を選択すると「土日だワッショイ!!」というメッセージが表示されます。
- 動作キャプチャ
- 曜日を選択するとメッセージが表示される
ストーリーの追加
サンプルコードのストーリーを追加する。
import Sample from '../Sample.vue';
import { within, userEvent } from '@storybook/testing-library';
const meta = {
title: 'Components/Sample',
component: Sample,
render: () => ({
components: { Sample },
template: '<Sample />',
}),
};
export default meta;
export const Default = {};
このストーリーだけだと、各曜日を選択した際に特定のメッセージが表示されるコンポーネント動作を機械的に確認できない。
ストーリー上でセレクトボックスを選択して目視での確認が必要。(コンポーネントのpropsの持たせ方を工夫すれば、初期描画時に選択された状態にできそうですが、選択肢の数だけストーリが増えるので面倒ですね。)
インタラクションの追加
ここでいうインタラクションとは、ストーリー上でクリックやフォーム入力の操作のことを指す。play関数
を定義することで、ストーリー上でのクリックやセレクトボックスの選択が定義できる。
play関数
:StorybookのCSF3.0から新機能として追加された関数。
...
...
export const Monday = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// セレクトボックスで月曜日を選択
await userEvent.selectOptions(canvas.getByRole('combobox'), 'Mon');
},
};
export const Tuesday = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.selectOptions(canvas.getByRole('combobox'), 'Tue');
},
};
export const Sunday = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.selectOptions(canvas.getByRole('combobox'), 'Sun');
},
};
上記のインタラクションが定義されたストーリーを追加することで、セレクトボックスが選択された結果の画面が描画される。
上記キャプチャでは全ての曜日のストーリーを定義している
インタラクションの操作結果はスナップショットに反映されないため、特定のメッセージが出力されていることを機械的に確認することはできていません。
(本題)@storybook/test-runnerでインタラクションテストを書く
1. 必要なライブラリのインポート
yarn add -D @storybook/test-runner @storybook/jest
2. インタラクションテストの追加
...
...
export const Monday = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.selectOptions(canvas.getByRole('combobox'), 'Mon');
// 月曜日を選択した場合は「Monday」が表示されること
await expect(await canvas.queryByText('Monday')).toBeTruthy();
},
};
export const Tuesday = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.selectOptions(canvas.getByRole('combobox'), 'Tue');
// 火曜日を選択した場合は「Tuesday」が表示されること
await expect(await canvas.queryByText('Tuesday')).toBeTruthy();
},
};
export const Sunday = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.selectOptions(canvas.getByRole('combobox'), 'Sun');
// 日曜日を選択した場合は「土日だワッショイ!!」が表示されること
await expect(await canvas.queryByText('土日だワッショイ!!')).toBeTruthy();
},
};
ここでは以下のインタクションテストを追加。
- 月曜日を選択した場合は Monday が表示されること
- 火曜日を選択した場合は Tuesday が表示されること
- 日曜日を選択した場合は 土日だワッショイ!! が表示されること
3. インタラクションテストの実行方法
- 実行コマンド
# 6006番ポートでStorybookを起動
yarn storybook dev --port 6006
# 起動しているStorybook上でtest-runnerを実行(インタラクションテストの実行)
yarn test-storybook --url http://localhost:6006
- 実行結果
$ test-storybook --url http://localhost:6006
PASS browser: chromium ../hogehoge/Sample.stories.js
Components/Sample
Default
✓ smoke-test (27 ms)
Monday
✓ play-test (34 ms)
Sunday
✓ play-test (7 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 3.172 s
Ran all test suites.
✨ Done in 5.93s.
テストが全て成功
- 試しにSample.vueのメッセージ内容を変更してインタクションテストを実行してみる
- (Sample.stories.jsのメッセージ内容は修正しない)
(省略...)
<div>
<select v-model="value">
<option value="" disabled>選択してください</option>
<option value="Mon">月曜日</option>
<option value="Tue">火曜日</option>
<option value="Wed">水曜日</option>
<option value="Thu">木曜日</option>
<option value="Fri">金曜日</option>
<option value="Sat">土曜日</option>
<option value="Sun">日曜日</option>
</select>
<div class="message">
<div v-if="value === 'Mon'">Monday</div>
<div v-if="value === 'Tue'">Tuesday</div>
<div v-if="value === 'Wed'">Wednesday</div>
<div v-if="value === 'Thu'">Thursday</div>
<div v-if="value === 'Fri'">Friday</div>
<div v-if="['Sat', 'Sun'].includes(value)" class="message_holiday">
- 土日だワッショイ!!
+ 土日だワッチョイ!!
</div>
</div>
</div>
...
- テスト結果
- インタラクションテストでは「土日だワッショイ!!」が出力されることを期待しているので、失敗するはず
yarn test-storybook --url http://localhost:6006
$ test-storybook --url http://localhost:6006
FAIL browser: chromium app/frontend/stories/components/Sample.stories.js
Components/Sample
Default
✓ smoke-test (27 ms)
Monday
✓ play-test (33 ms)
Sunday
✕ play-test (14 ms)
Sunday(日曜日を選択した場合)のテストコードが失敗した。👍
まとめ
- Storybookを利用してユーザー操作によるUIテストを行えた
- Storybookを起動しないでインタラクションテストできるとより良い(要調査)
- CIでインタラクションテストを実行することは可能