Testing - Vueアプリの品質を守る3層戦略

ロードマップ: Vue.js学習ロードマップ

What(何についてか)

Testing は、Vueアプリケーションを安全に変更し続けるための品質保証戦略である。自動テストはリグレッションを防ぐだけでなく、関数、モジュール、composable、component をテスト可能な形へ分解する圧力としても機能する。

Vue公式は、テスト戦略を次の3層で整理している。

  • Unit Testing
  • Component Testing
  • End-to-End Testing

これは優劣ではなく、守る対象が異なる別種の検査である。

Why(なぜ必要か)

アプリケーションは、ロジック、UI、画面遷移、ネットワーク連携など多様な壊れ方をする。テストを導入することで、変更時の退行を早期に検知し、チームが安心して機能追加やリファクタリングを進められる。

また、Testing は単なる後付けの検査ではない。テストしづらいコードは責務分離が悪い可能性が高く、Testing は設計改善のフィードバック装置にもなる。

Vue公式は、テストはできるだけ早く書き始めることを推奨している。後から導入するほど依存が増え、テストを追加しにくくなるためである。

How(どう動くか)

Testing strategy の3層

flowchart TD
  A["Unit Testing"] --> AU["小さなロジックの正しさ"]
  B["Component Testing"] --> BU["UI部品の振る舞い"]
  C["E2E Testing"] --> CU["アプリ全体の利用フロー"]

Unit Testing

Unit test は、関数、class、module、composable のような小さく独立した単位を検証する。目的は business logic の正しさを速く、局所的に検証することにある。

export function increment(current, max = 10) {
  if (current < max) {
    return current + 1
  }
  return current
}
import { increment } from './helpers'
 
describe('increment', () => {
  test('increments the current number by 1', () => {
    expect(increment(0, 10)).toBe(1)
  })
 
  test('does not increment the current number over the max', () => {
    expect(increment(10, 10)).toBe(10)
  })
 
  test('has a default max of 10', () => {
    expect(increment(10)).toBe(10)
  })
})

Vue特有の unit test 対象としては composable が重要である。lifecycle や mount を伴うものは特別な扱いが必要な場合もあるが、再利用ロジックを切り出しておくほど unit test しやすくなる。

Component Testing

Component test は、Vueアプリの主要構成要素である component の振る舞いを検証する。粒度としては unit test より一段広く、integration test 的な性格を持つ。

主に検証対象となるのは次の public interface である。

  • props
  • emitted events
  • slots
  • DOM output
  • user interaction に対する反応

設計姿勢として重要なのは、component を blackbox に扱うことである。内部 state や private method を直接検査するのではなく、入力に対してユーザーから見える出力がどう変わるかを検証する。

flowchart LR
  I["props / slots / user interaction"] --> C["Component"] --> O["DOM / emitted events"]

例えば Stepper component であれば、内部実装ではなく、max prop を渡した状態で increment ボタンをクリックした結果、表示値が正しく更新されるかを見る。

テストの観点

  • Visual logic: props や slots に応じた描画結果を検証する
  • Behavioral logic: user action に対する DOM 更新や emitted event を検証する

避けるべきこと

  • private state の直接検査
  • private method の直接テスト
  • 実装詳細に依存した brittle なテスト
  • snapshot test のみに依存すること

End-to-End Testing

E2E test は、ユーザーが実際に利用するフローをアプリ全体で検証する。複数ページにまたがる画面遷移や、本番に近いブラウザ挙動、ネットワーク連携を含めた統合確認が対象になる。

unit test や component test では局所的な正しさは守れるが、router、state、UI、API が組み合わさった時の破綻は E2E でないと検知しにくい。

一方で、E2E は実行コストが高く、遅く、環境依存も増える。そのため、全機能を E2E で守るのではなく、ログイン、主要CRUD、重要な画面遷移など、クリティカルなユーザーフローへ絞るのが基本になる。

Vueにおけるツール選定

Unit Testing

  • Vitest を推奨
  • 理由: Vite の transform pipeline と設定をそのまま使え、導入が軽く高速だから
  • Jest は既存資産がある場合の移行先候補であり、新規Viteプロジェクトでは第一候補ではない

Component Testing

  • Vitest + @vue/test-utils
    • headless な component や、Nodeベースで十分なDOMテスト向き
  • Cypress Component Testing
    • style、ネイティブDOMイベント、ブラウザ実挙動の確認が重要な場合に向く

End-to-End Testing

  • Playwright を推奨
    • Chromium / WebKit / Firefox 対応
    • 高い debuggability
    • traces、parallelization、headless / headed 実行を備える
  • Cypress も強力な選択肢
    • GUIやデバッグ体験が良い
    • Component Testing との接続も強い

Options API → Composition API 差分(補足)

項目Options API(旧)Composition API(新)
テスト対象のロジック切り出しmethods / computed / mixins に分散しやすいcomposable や module に切り出しやすい
UIロジックの再利用単位mixins 中心composable 中心
テストしやすさthis 依存が混ざりやすいpure function / composable に寄せやすい
推奨unit基盤Jest文化が多かったVitest + Vite が自然

Key Concepts

用語説明
Unit Testing小さく独立したロジック単位を検証するテスト
Component Testingcomponent の公開インターフェースと振る舞いを検証するテスト
E2E Testingアプリ全体のユーザーフローを検証するテスト
blackbox testing実装詳細ではなく入力と出力の振る舞いを見る観点
public interfaceprops、events、slots、DOM など外から観測可能な契約
regression変更により既存挙動が壊れること
brittle test実装変更だけで壊れやすい不安定なテスト
VitestVite ベースプロジェクト向けの高速テストフレームワーク
Vue Test UtilsVue component のマウントと操作を支援するテストユーティリティ
Playwrightモダンな E2E テスト基盤
CypressE2E と component testing を支援するブラウザベースのテスト基盤

実務での判断軸

  • business logic はできるだけ component から切り離し、unit test しやすい構造へ寄せる
  • Vueでは component test が中核であり、blackbox 発想で public interface を検証する
  • E2E は高コストなので、重要フローに絞って配置する
  • Testing は品質保証だけでなく、責務分離と設計健全性を保つための仕組みでもある