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問の回答を。