← スキル一覧に戻る
unit-test
tqer39 / blog
⭐ 0🍴 0📅 2026年1月17日
単体テストの作成。「テストを書きたい」「ユニットテストを追加」「関数をテスト」などのリクエスト時に使用。
SKILL.md
---
name: unit-test
description: 単体テストの作成。「テストを書きたい」「ユニットテストを追加」「関数をテスト」などのリクエスト時に使用。
---
# Unit Test
コンポーネント・関数の単体テストを作成するスキル。
## 推奨テストフレームワーク
- **Vitest**: 高速なテストランナー(Vite ベース)
- **React Testing Library**: コンポーネントテスト
- **@testing-library/user-event**: ユーザー操作シミュレーション
## セットアップ(未設定の場合)
```bash
pnpm add -D vitest @vitejs/plugin-react jsdom
pnpm add -D @testing-library/react @testing-library/jest-dom @testing-library/user-event
```
### vitest.config.ts
```typescript
import react from "@vitejs/plugin-react";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
globals: true,
setupFiles: ["./vitest.setup.ts"],
include: ["src/**/*.{test,spec}.{ts,tsx}"],
},
resolve: {
alias: {
"@": "./src",
},
},
});
```
### vitest.setup.ts
```typescript
import "@testing-library/jest-dom/vitest";
```
## テストファイル配置
```text
src/
├── components/
│ ├── Button.tsx
│ └── Button.test.tsx # コンポーネントと同階層
├── lib/
│ ├── utils.ts
│ └── utils.test.ts # ユーティリティと同階層
└── __tests__/ # 統合テスト用(オプション)
```
## テストテンプレート
### コンポーネントテスト
```typescript
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { Button } from "./Button";
describe("Button", () => {
it("renders with text", () => {
render(<Button>Click me</Button>);
expect(screen.getByRole("button", { name: /click me/i })).toBeInTheDocument();
});
it("calls onClick when clicked", async () => {
const user = userEvent.setup();
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("is disabled when disabled prop is true", () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByRole("button")).toBeDisabled();
});
});
```
### 関数テスト
```typescript
import { describe, expect, it } from "vitest";
import { formatDate, slugify } from "./utils";
describe("formatDate", () => {
it("formats date correctly", () => {
expect(formatDate("2025-01-01")).toBe("2025年1月1日");
});
it("handles invalid date", () => {
expect(formatDate("invalid")).toBe("Invalid Date");
});
});
describe("slugify", () => {
it("converts string to slug", () => {
expect(slugify("Hello World")).toBe("hello-world");
});
});
```
### 非同期テスト
```typescript
import { render, screen, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { AsyncComponent } from "./AsyncComponent";
describe("AsyncComponent", () => {
it("shows loading state initially", () => {
render(<AsyncComponent />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
});
it("shows data after loading", async () => {
render(<AsyncComponent />);
await waitFor(() => {
expect(screen.getByText(/data loaded/i)).toBeInTheDocument();
});
});
});
```
## テスト実行コマンド
```bash
# 全テスト実行
pnpm test
# ウォッチモード
pnpm test:watch
# カバレッジ付き
pnpm test:coverage
# 特定ファイル
pnpm test src/lib/utils.test.ts
```
## package.json scripts
```json
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage"
}
}
```
## テスト作成の手順
1. テスト対象のコード確認
2. テストケースの洗い出し
- 正常系
- 異常系
- 境界値
3. テストファイル作成(`*.test.ts(x)`)
4. describe でグループ化
5. it/test で個別ケース記述
6. テスト実行・確認
## ベストプラクティス
- **AAA パターン**: Arrange(準備)→ Act(実行)→ Assert(検証)
- **1テスト1検証**: 各テストは1つの振る舞いを検証
- **実装詳細をテストしない**: 内部実装ではなく振る舞いをテスト
- **getByRole 優先**: アクセシビリティを意識したクエリ
- **ユーザー視点**: ユーザーの操作をシミュレート
## クエリ優先順位
1. `getByRole` - アクセシビリティ
2. `getByLabelText` - フォーム要素
3. `getByPlaceholderText` - 入力フィールド
4. `getByText` - テキストコンテンツ
5. `getByTestId` - 最終手段