3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Vue.js + Vitest】実務で活きる単体テストと結合テストの使い分けをタスク管理アプリで学ぶ

3
Posted at

フロントエンド開発において、自動テストを書くことは品質を担保するために欠かせません。しかし、「何を単体テストで書き、何を結合テストで書くべきか」という境界線に迷うことはないでしょうか?

この記事では、Vue.js (Composition API) + TypeScript + Vitest で構築した「タスク管理アプリ (Task Manager)」の実装を題材に、単体テスト と 結合テスト の違いと、それぞれの役割、そして現場で求められる実践的な使い分けについて解説します。

実際に作ったタスク管理アプリ:
https://github.com/sisisa/test-learn

1. 単体テストとは?

単体テストは、アプリを構成する 「最小の部品(関数やクラス)」が、単独で正しく動くか を確認するテストです。

UIの描画、データベース、外部API通信などの他の部品には一切依存せず、「Aという入力を入れたら、絶対にBという出力が返ってくるか」だけを純粋に検証します。

このアプリでの具体例

タスク管理アプリにおける、タスクのタイトルをチェックする関数 validateTitle のテストがわかりやすい例です。

// tests/unit/validator.spec.ts より抜粋
describe('validateTitle', () => {
  it('有効なタイトルでundefined(エラーなし)を返す', () => {
    expect(validateTitle('買い物リスト')).toBeUndefined()
  })

  it('空文字でエラーメッセージを返す', () => {
    // UIがどうなっているかは関係なく、関数に空文字を渡した結果だけを見る
    expect(validateTitle('')).toBe('タイトルは必須です')
  })
})

単体テストの特徴と役割

  • 速い・確実: ブラウザを起動したり画面を描画したりしないため、一瞬で終わります。
  • 原因箇所の特定が容易: もしこのテストが落ちたら、「validator.tsvalidateTitle 関数がおかしい」とすぐに特定できます。
  • 実務での役割: 「日付の計算」「金額の消費税計算」「パスワードの文字数チェック」など、絶対に間違ってはいけない コアなビジネスロジック をガチガチに守るために書きます。

2. 結合テストとは?

結合テストは、単体テストで安全が確認された複数の部品を 組み合わせて(結合して)、連携して正しく動くか を確認するテストです。

フロントエンド開発の場合、「画面(UI)」と「裏側のロジック」が意図通りに連動しているかを見るのが主な役割になります。

このアプリでの具体例

タスク追加フォームコンポーネント (src/components/TaskForm.vue) のテストがまさにこれに当たります。Vue Test Utilsを使ってユーザーの操作をシミュレートします。

// tests/integration/TaskForm.spec.ts より抜粋
it('空のまま送信するとタイトルエラーが表示される', async () => {
  // 1. フォームコンポーネントを仮想的に画面に描画する
  const wrapper = mount(TaskForm)

  // 2. ユーザーが「追加する(送信)」ボタンを押した操作をシミュレート
  await wrapper.find('[data-testid="task-form"]').trigger('submit')

  // 3. 画面上に「タイトルは必須です」というエラー文言が表示されたか確認
  expect(wrapper.find('[data-testid="error-title"]').text()).toContain('必須')
})

このテストの裏側では、以下のように複数の部品が連動して動いています。

  1. ユーザーがボタンを押す(UI操作)
  2. useFormValidation.ts の制御が走る(Composableのロジック)
  3. validator.tsvalidateTitle 関数が呼ばれてエラーを返す(先ほどの単体テスト箇所)
  4. エラー文言が画面に描画される(UIの更新)

結合テストの特徴と役割

  • ユーザー目線: 「ボタンを押したらエラーが出る」「一覧が絞り込まれる」など、ユーザーが実際に体験するフローをテストします。
  • 部品間のズレを検知: 「関数単体は正しいのに、画面に渡す変数を間違えていて表示されない」といった、結合時のミス(インターフェースの不整合)を防ぎます。
  • 実務での役割: 「ログインフロー」「カートへの商品追加」「検索フィルターの動作」といった、ユーザーにとって重要な機能全体 が壊れていないかを担保するために書きます。

各テスト一覧:

テスト種類 対象 目的 このアプリでの対象
単体テスト 最小部品(関数など) 個々のロジックが完璧に正しいことの証明。 validator, formatter, taskFilter, taskApi など
結合テスト 組み合わせ(UI + ロジック) ユーザーが画面を操作した際の一連の動きの証明。 TaskForm, TaskList, TaskItem など

まとめ

実務において評価されるのは、「これらのテストの使い分けが論理的にできているか?」 という点です。

「すべてのテストを結合テスト(UI操作)でやろうとすると、テストの実行が遅くなりすぎてメンテナンスが破綻する」という事実があります。逆に「単体テストだけだと、つなぎ合わせた時に画面がちゃんと動く保証がない」という問題も生じます。

このアプリでは、「複雑なロジック(日付処理やバリデーション)は単体テストで高速かつ徹底的に叩き、全体が繋がったユーザー体験は結合テストで重要経路のみを抑える」 という、現場で最も評価される実用的なバランス(いわゆる「テストピラミッド」の考え方)を意図して構成しています。

これからテストを導入しようとしている方は、ぜひこの「関数のロジック担保」と「UIのユーザー体験担保」の使い分けを意識してみてください。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?