unit-test-vue-pinia について
Vue コンポーネント、composable、Pinia ストアのユニットテストを作成・レビューするスキルです。観察可能な振る舞いを検証する小さく決定的なテストと、最小限の Pinia 設定を案内します。
主な機能
- 01振る舞いを軸にしたテスト設計
- 02Pinia 設定とアクションのスタブ指針
- 03Vue Test Utils の操作パターン
- 04観察可能な出力と副作用の検証
- 05カバレッジ不足や脆弱なテストの確認
活用シーン
- 01Vue コンポーネントの焦点を絞ったテスト作成
- 02composable や Pinia ストアの動作テスト
- 03脆弱なテストや内部実装依存のアサーションのレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ github/ awesome-copilot --skill unit-test-vue-piniaインストール後、Claude Code で/unit-test-vue-piniaを入力して読み込まれたか確認します。
同梱ファイルreferences/pinia-patterns.md
詳細
unit-test-vue-pinia
このスキルは、Vue コンポーネント、コンポーザブル、Pinia ストアのユニットテストを作成またはレビューする際に使用します。テストは小さく、決定的で、振る舞いを重視したものにしてください。
ワークフロー
- まず、コンポーネントの UI の振る舞い、コンポーザブルの振る舞い、ストアの振る舞いのどれをテストするのかを特定します。
- その振る舞いを証明できる、最も範囲の狭いテストスタイルを選びます。
- シナリオをカバーできる範囲で、最小限の Pinia をセットアップします。
- props、フォームの更新、ボタンクリック、子コンポーネントからの emit イベント、ストア API などの公開入力を通じてテストを実行します。
- インスタンスレベルのアサーションを検討する前に、観測可能な出力と副作用をアサートします。
- 振る舞いが明確に分かる名前を付けたテストを提示またはレビューし、残っているカバレッジの不足点を記載します。
基本ルール
- 1 つのテストにつき、1 つの振る舞いをテストします。
- まず、観測可能な入出力の振る舞い(レンダリングされたテキスト、emit イベント、コールバックの呼び出し、ストアの状態変化)をアサートします。
- 実装に依存するアサーションは避けます。
- 妥当な DOM、prop、emit、またはストアレベルのアサーションができない場合に限り、例外的に
wrapper.vmにアクセスします。 beforeEach()で明示的にセットアップし、各テストでモックをリセットします。- Pinia の標準的なテストセットアップでは、チェックイン済みの参考資料
references/pinia-patterns.mdをローカルの信頼できる情報源として使用します。
Pinia のテスト方法
まず references/pinia-patterns.md を参照し、チェックイン済みの例でカバーされていないケースについては Pinia のテスト用クックブックを参照します。
コンポーネントテストのデフォルトパターン
マウント時に、グローバルプラグインとして createTestingPinia を使用します。
一貫性を保ち、アクションのスパイに関するアサーションを容易にするため、デフォルトでは createSpy: vi.fn を使用してください。
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
}),
],
},
});
デフォルトでは、アクションはスタブ化され、スパイされます。
アクションが呼び出されたか(または呼び出されなかったか)の確認だけが必要なテストでは、stubActions: true(デフォルト)を使用します。
使用可能な最小限の Pinia セットアップ
以下も有効であり、誤りとして指摘すべきではありません。
- テストで Pinia アクションのスパイ動作をアサートしない場合は、
createTestingPinia({})を使用します。 - 生成されたスパイを調べず、状態の初期化またはアクションのスタブ化だけが必要なテストでは、
createSpyを指定せずにcreateTestingPinia({ initialState: ... })またはcreateTestingPinia({ stubActions: ... })を使用します。 - 依存ストアをモックまたは初期化する必要がある場合は、コンポーネントをマウントせずに、ストアまたはコンポーザブルを中心としたテストで
setActivePinia(createTestingPinia(...))を使用します。
アクションのスパイに関するアサーションがテストの目的に含まれる場合は、createSpy: vi.fn を使用します。
必要な場合に限り、実際のアクションを実行する
アクションの実際の振る舞いと副作用を検証する必要がある場合に限り、stubActions: false を使用します。「呼び出されたかどうか」だけを確認する単純なアサーションのために、デフォルトで有効にしないでください。
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
stubActions: false,
}),
],
},
});
initialState でストアの状態を初期化する
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
initialState: {
counter: { n: 20 },
user: { name: "Leia Organa" },
},
}),
],
},
});
createTestingPinia を通じて Pinia プラグインを追加する
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
plugins: [myPiniaPlugin],
}),
],
},
});
エッジケース向けのゲッター上書きパターン
const pinia = createTestingPinia({ createSpy: vi.fn });
const store = useCounterStore(pinia);
store.double = 999;
// @ts-expect-error test-only reset of overridden getter
store.double = undefined;
純粋なストアのユニットテスト
コンポーネントをレンダリングせずにストアの状態遷移とアクションの振る舞いを検証する場合は、createPinia() を使用した純粋なストアテストを優先します。依存ストアのスタブ化、テスト用ダブルの初期化、またはアクションのスパイが必要な場合に限り、createTestingPinia() を使用します。
beforeEach(() => {
setActivePinia(createPinia());
});
it("increments", () => {
const counter = useCounterStore();
counter.increment();
expect(counter.n).toBe(1);
});
Vue Test Utils の方法
Vue Test Utils のガイダンスに従ってください:https://test-utils.vuejs.org/guide/
- 焦点を絞ったユニットテストでは、デフォルトでシャローでマウントします。
- インテグレーションの振る舞いを対象とする場合に限り、コンポーネントツリー全体をマウントします。
- props、ユーザーの操作に近いインタラクション、emit イベントを通じて振る舞いを実行します。
- 親コンポーネントの内部に触れるのではなく、子コンポーネントのスタブイベントには
findComponent(...).vm.$emit(...)を優先して使用します。 - 更新が非同期の場合に限り、
nextTickを使用します。 wrapper.emitted(...)で emit イベントとペイロードをアサートします。- DOM、emit イベント、prop、またはストアレベルのアサーションで振る舞いを表現できない場合に限り、
wrapper.vmにアクセスします。例外として扱い、アサーションの範囲を狭く保ってください。
主なテスト用スニペット
ペイロードを送信してアサートする:
await wrapper.find("button").trigger("click");
expect(wrapper.emitted("submit")?.[0]?.[0]).toBe("Mango Mission");
入力を更新して出力をアサートする:
await wrapper.find("input").setValue("Agent Violet");
await wrapper.find("form").trigger("submit");
expect(wrapper.emitted("save")?.[0]?.[0]).toBe("Agent Violet");
テスト作成のワークフロー
- テスト対象の振る舞いの境界を特定します。
- 最小限のフィクスチャデータを用意します(その振る舞いに必要なフィールドのみ)。
- Pinia と必要なテストダブルを設定します。
- 公開入力を通じて振る舞いを実行します。
- 公開出力と副作用を検証します。
- テスト名を、実装ではなく振る舞いを説明する名前にリファクタリングします。
制約と安全性
- プライベートな実装の詳細をテストしないでください。
- 動的な UI の振る舞いにスナップショットを多用しないでください。
- 1 つの振る舞いだけが重要な場合、大きなオブジェクトのすべてのフィールドを検証しないでください。
- フェイクデータは決定論的にし、ランダムな値を避けてください。
- 上記の許容される最小限のセットアップのいずれかである場合、Pinia のセットアップが誤っていると指摘しないでください。
- テスト対象の振る舞いに追加の範囲が必要な場合を除き、動作しているテストを、より深いマウントや実際のアクションを使う形に書き換えないでください。
- レビューでは、テストカバレッジの不足、壊れやすいセレクター、実装に依存したアサーションを明示的に指摘してください。
出力の契約
createまたはupdateの場合は、完成したテストコードに加えて、選択した Pinia 戦略を説明する短い注記を返してください。reviewの場合は、具体的な指摘を最初に示し、その後にカバレッジ不足や壊れやすさのリスクを示してください。- 最も安全な選択が曖昧な場合は、選択したテストセットアップの根拠となった前提を明記してください。
参考資料
references/pinia-patterns.md- Pinia テストクックブック:https://pinia.vuejs.org/cookbook/testing.html
- Vue Test Utils ガイド:https://test-utils.vuejs.org/guide/