採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

Vue — vitest

テクノロジ系 / L07 test — テスト

Vue — vitest

install

$ pnpm add -D vitest @vue/test-utils jsdom @vitejs/plugin-vue

vitest.config.ts:

import { defineConfig } from "vitest/config";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [vue()],
  test: {
    environment: "jsdom",
    globals: true,
  },
});

composable のテスト

// composables/useCounter.test.ts
import { describe, it, expect } from "vitest";
import { useCounter } from "./useCounter.js";

describe("useCounter", () => {
  it("starts at 0", () => {
    const { count } = useCounter();
    expect(count.value).toBe(0);
  });

  it("increments", () => {
    const { count, increment } = useCounter();
    increment();
    expect(count.value).toBe(1);
  });

  it("resets to initial", () => {
    const { count, increment, reset } = useCounter(5);
    increment();
    expect(count.value).toBe(6);
    reset();
    expect(count.value).toBe(5);
  });
});

composable は コンポーネント無しで テストできる。

純粋関数のテスト

// utils/format.test.ts
import { describe, it, expect } from "vitest";
import { formatCurrency } from "./format.js";

describe("formatCurrency", () => {
  it("formats integer", () => {
    expect(formatCurrency(1234)).toBe("¥1,234");
  });

  it("handles zero", () => {
    expect(formatCurrency(0)).toBe("¥0");
  });
});

依存なしの関数なら一番楽。

モック

import { vi, it } from "vitest";

it("calls fetch", async () => {
  global.fetch = vi.fn().mockResolvedValue({
    ok: true,
    json: async () => ({ id: 1 }),
  });

  const user = await getUser(1);
  expect(user.id).toBe(1);
});

カバレッジ

$ pnpm add -D @vitest/coverage-v8
$ pnpm test --coverage

coverage/index.html を開くと行ごとのカバレッジが見える。

まとめ

  • vitest を Vite ベースで導入
  • composable / 純粋関数は単独テスト容易
  • モックは vi.fn() / vi.mock()
  • カバレッジは @vitest/coverage-v8

次は コンポーネントのテスト

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。

Lv1: 基本 — composable test

問題

// composables/useCounter.test.ts
import { describe, it, expect } from "vitest";
import { useCounter } from "./useCounter.js";

describe("useCounter", () => {
  it("starts at 0", () => {
    const { count } = useCounter();
    expect(count.value).toBe(0);
  });

  it("increments", () => {
    const { count, inc } = useCounter();
    inc();
    expect(count.value).toBe(1);
  });

  it("resets to initial", () => {
    const { count, inc, reset } = useCounter(5);
    inc();
    reset();
    expect(count.value).toBe(5);
  });
});

合格基準

3 test pass。

Lv2: 応用 — 純粋関数のテスト

問題

// utils/format.ts
export function formatCurrency(n: number): string {
  return `¥${n.toLocaleString()}`;
}
export function pluralize(count: number, word: string): string {
  return count === 1 ? word : `${word}s`;
}

format.test.ts で 5 test 以上書く。

合格基準

  • 5 以上の test
  • 境界値 (0 / 1 / 大数) を含む

Lv3: 発展 — モック (fetch)

問題

import { vi, beforeEach, afterEach } from "vitest";

beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); });
afterEach(() => { vi.unstubAllGlobals(); });

it("getUser calls fetch", async () => {
  (fetch as any).mockResolvedValue({
    ok: true,
    json: async () => ({ id: 1, name: "Taro" }),
  });

  const user = await getUser(1);
  expect(user.name).toBe("Taro");
  expect(fetch).toHaveBeenCalledWith("/api/users/1");
});

合格基準

  • fetch モック
  • 呼び出し引数の検証

提出

submission.md に3問の回答を。