test-automation-expert について
テスト戦略、自動テストの実装、カバレッジ改善、フレークするテストの調査を支援するスキルです。JavaScript/TypeScript と Python の例を交え、単体・統合・E2E テストを扱います。
主な機能
- 01テスト戦略の策定
- 02単体・統合・E2E テストの作成
- 03テストフレームワークの選定
- 04カバレッジ向上と不足箇所の特定
- 05フレークするテストの調査
- 06CI/CD テストパイプラインの設定
活用シーン
- 01新規プロジェクトのテスト導入
- 02重要なユーザーフローのテスト
- 03不安定な自動テストの原因調査
- 04CI/CD へのテスト追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill test-automation-expertインストール後、Claude Code で/test-automation-expertを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.md.claude-plugin/plugin.jsonreferences/ci-integration.mdcoverage-patterns.mdframework-comparison.mdtest-strategy.md詳細
テスト自動化エキスパート
ユニットテストから E2E テストまで、包括的なテストガイダンスを提供します。テスト戦略の設計、自動化の実装、持続可能な品質に向けたカバレッジの最適化を行います。
使用する場面
次の用途に使用します:
- 新規プロジェクトのテスト戦略の設計
- テストフレームワーク(Jest、Vitest、Playwright、Cypress、pytest)のセットアップ
- 効果的なユニットテスト、統合テスト、E2E テストの作成
- テストカバレッジの最適化と漏れの解消
- 不安定なテストのデバッグ
- CI/CD テストパイプラインの設定
- テスト駆動開発(TDD)のガイダンス
- モック戦略とテストフィクスチャ
次の用途には使用しないでください:
- 手動 QA テストケースの作成 — 自動化に特化しています
- 負荷・パフォーマンステスト — performance-engineer スキルを使用してください
- セキュリティテスト — security-auditor スキルを使用してください
- API コントラクトテストのみ — API 設計には backend-architect を使用してください
テストピラミッドの理念
/\
/ \ E2E Tests (10%)
/----\ - Critical user journeys
/ \ - Cross-browser validation
/--------\
/ \ Integration Tests (20%)
/ \ - API contracts
/--------------\- Component interactions
/ \
/------------------\ Unit Tests (70%)
- Fast, isolated, deterministic
- Business logic validation
配分のガイドライン
| テストの種類 | 割合 | 実行時間 | 目的 |
|---|---|---|---|
| ユニット | 70% | < 100ms 各 | ロジックの検証 |
| 統合 | 20% | < 1s 各 | コンポーネントの契約 |
| E2E | 10% | < 30s 各 | 重要なパス |
フレームワークの選択
JavaScript/TypeScript
| フレームワーク | 最適な用途 | 速度 | 設定の複雑さ |
|---|---|---|---|
| Vitest | Vite プロジェクト、最新の ESM | 最速 | 低 |
| Jest | React、実績のあるプロジェクト | 高速 | 中 |
| Playwright | E2E、クロスブラウザー | 該当なし | 低 |
| Cypress | E2E、コンポーネントテスト | 該当なし | 中 |
Python
| フレームワーク | 最適な用途 | 速度 | 機能 |
|---|---|---|---|
| pytest | あらゆる用途 | 高速 | フィクスチャ、プラグイン |
| unittest | 標準ライブラリ | 中 | 組み込み |
| hypothesis | プロパティベーステスト | 変動 | 生成 |
判断ツリー:フレームワークの選択
New project?
├── Yes → Using Vite?
│ ├── Yes → Vitest
│ └── No → Jest or Vitest (both work)
└── No → What exists?
├── Jest → Keep Jest (migration cost rarely worth it)
├── Mocha → Consider migration to Vitest
└── Nothing → Vitest (modern default)
Need E2E?
├── Cross-browser critical → Playwright
├── Developer experience priority → Cypress
└── Both → Playwright (more flexible)
ユニットテストのパターン
優れたユニットテストの構成
describe('UserService', () => {
describe('validateEmail', () => {
// Arrange-Act-Assert pattern
it('should accept valid email formats', () => {
// Arrange
const validEmails = ['[email protected]', '[email protected]'];
// Act & Assert
validEmails.forEach(email => {
expect(validateEmail(email)).toBe(true);
});
});
it('should reject invalid email formats', () => {
// Arrange
const invalidEmails = ['invalid', '@missing.com', 'no@tld'];
// Act & Assert
invalidEmails.forEach(email => {
expect(validateEmail(email)).toBe(false);
});
});
// Edge cases explicitly tested
it('should handle empty string', () => {
expect(validateEmail('')).toBe(false);
});
it('should handle null/undefined', () => {
expect(validateEmail(null)).toBe(false);
expect(validateEmail(undefined)).toBe(false);
});
});
});
モック戦略
// ✅ Good: Mock at boundaries
jest.mock('../services/api', () => ({
fetchUser: jest.fn()
}));
// ✅ Good: Explicit mock setup per test
beforeEach(() => {
fetchUser.mockReset();
});
it('handles user not found', async () => {
fetchUser.mockRejectedValue(new NotFoundError());
await expect(getUser(123)).rejects.toThrow('User not found');
});
// ❌ Bad: Mocking implementation details
jest.mock('../utils/internal-helper'); // Don't mock internals
テスト分離のチェックリスト
- 各テストを個別に実行できる
- テスト間で可変状態を共有しない
- テスト間でデータベース/API の状態をリセットする
- テストの実行順序に依存しない
- 並列実行しても安全である
統合テストのパターン
API 統合テスト
describe('POST /api/users', () => {
let app;
let db;
beforeAll(async () => {
db = await createTestDatabase();
app = createApp({ db });
});
afterAll(async () => {
await db.close();
});
beforeEach(async () => {
await db.clear();
});
it('creates user with valid data', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'Test', email: '[email protected]' })
.expect(201);
expect(response.body).toMatchObject({
id: expect.any(String),
name: 'Test',
email: '[email protected]'
});
// Verify side effects
const dbUser = await db.users.findById(response.body.id);
expect(dbUser).toBeDefined();
});
it('rejects duplicate email', async () => {
await db.users.create({ name: 'Existing', email: '[email protected]' });
await request(app)
.post('/api/users')
.send({ name: 'New', email: '[email protected]' })
.expect(409);
});
});
コンポーネント統合(React)
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserProfile } from './UserProfile';
import { UserProvider } from '../context/UserContext';
describe('UserProfile integration', () => {
it('loads and displays user data', async () => {
render(
<UserProvider>
<UserProfile userId="123" />
</UserProvider>
);
// Verify loading state
expect(screen.getByRole('progressbar')).toBeInTheDocument();
// Wait for data
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
// Verify loaded state
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
});
});
E2E テストのパターン
Playwright のベストプラクティス
import { test, expect } from '@playwright/test';
test.describe('Checkout Flow', () => {
test.beforeEach(async ({ page }) => {
// Seed test data via API
await page.request.post('/api/test/seed', {
data: { scenario: 'checkout-ready' }
});
});
test('complete purchase with credit card', async ({ page }) => {
await page.goto('/cart');
// Use accessible selectors
await page.getByRole('button', { name: 'Proceed to checkout' }).click();
// Fill payment form
await page.getByLabel('Card number').fill('4242424242424242');
await page.getByLabel('Expiry').fill('12/25');
await page.getByLabel('CVC').fill('123');
// Complete purchase
await page.getByRole('button', { name: 'Pay now' }).click();
// Verify success
await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();
await expect(page.getByText(/Order #\d+/)).toBeVisible();
});
test('shows error for declined card', async ({ page }) => {
await page.goto('/checkout');
// Use test card that triggers decline
await page.getByLabel('Card number').fill('4000000000000002');
await page.getByLabel('Expiry').fill('12/25');
await page.getByLabel('CVC').fill('123');
await page.getByRole('button', { name: 'Pay now' }).click();
await expect(page.getByRole('alert')).toContainText('Card declined');
});
});
不安定なテストの検出と防止
一般的な原因:
- 非同期処理での競合状態
- 時間に依存するテスト
- テスト間で共有される状態
- ネットワークの変動
- アニメーションやトランジションのタイミング
修正方法:
// ❌ Bad: Fixed timeout
await page.waitForTimeout(2000);
// ✅ Good: Wait for specific condition
await expect(page.getByText('Loaded')).toBeVisible();
// ❌ Bad: Checking exact time
expect(new Date()).toEqual(specificDate);
// ✅ Good: Mock time
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-15'));
// ❌ Bad: Depending on animation completion
await page.click('.button');
expect(await page.isVisible('.modal')).toBe(true);
// ✅ Good: Wait for animation
await page.click('.button');
await expect(page.locator('.modal')).toBeVisible();
カバレッジの最適化
測定対象
| 指標 | 目標 | 優先度 |
|---|---|---|
| 行カバレッジ | 80%+ | 中 |
| 分岐カバレッジ | 75%+ | 高 |
| 関数カバレッジ | 90%+ | 中 |
| 重要なパスのカバレッジ | 100% | 最高 |
カバレッジの設定
// vitest.config.js
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'test/',
'**/*.d.ts',
'**/*.config.*',
'**/index.ts', // barrel files
],
thresholds: {
branches: 75,
functions: 80,
lines: 80,
statements: 80
}
}
}
});
カバレッジの漏れを見つける
# Generate detailed coverage report
npx vitest run --coverage
# Find untested files
npx vitest run --coverage --reporter=json | jq '.coverageMap | to_entries | map(select(.value.s | values | any(. == 0))) | .[].key'
CI/CD との統合
GitHub Actions
name: Tests
on: [push, pull_request]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- uses: codecov/codecov-action@v4
e2e-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
テストの並列化
// vitest.config.js - parallel by default
export default defineConfig({
test: {
pool: 'threads',
poolOptions: {
threads: {
singleThread: false
}
}
}
});
// playwright.config.js
export default defineConfig({
workers: process.env.CI ? 2 : undefined,
fullyParallel: true
});
アンチパターン
アンチパターン:実装の詳細をテストする
次のような例です:
// ❌ Testing internal state
expect(component.state.isLoading).toBe(true);
// ❌ Testing private methods
expect(service._calculateHash()).toBe('abc123');
問題点: テストが実装に密結合し、リファクタリングで壊れます
代わりに:
// ✅ Test observable behavior
expect(screen.getByRole('progressbar')).toBeInTheDocument();
// ✅ Test public interface
expect(service.getHash()).toBe('abc123');
アンチパターン:過剰なモック
次のような例です:
// ❌ Mocking everything
jest.mock('../utils/format');
jest.mock('../utils/validate');
jest.mock('../utils/transform');
問題点: 実際のコードが壊れていてもテストが成功します
代わりに: システム境界(API、データベース、外部サービス)でのみモックします
アンチパターン:不安定な受け入れテスト
次のような例です: 「あのテストは不安定だから、スキップしよう」
問題点: 不安定なテストは実際の問題(競合状態、タイミングの問題)を示しています
代わりに: 不安定さを修正するか、修正中は隔離します
アンチパターン:カバレッジの見せかけ
次のような例です:
// ❌ Testing for coverage, not behavior
it('covers the function', () => {
myFunction();
// No assertions!
});
問題点: 信頼性がゼロなのにカバレッジは 100% です
代わりに: すべてのテストで、意味のある振る舞いをアサートします
クイックコマンド
# Run all tests
npm test
# Run with coverage
npm test -- --coverage
# Run specific file
npm test -- src/utils/format.test.ts
# Run in watch mode
npm test -- --watch
# Run E2E tests
npx playwright test
# Run E2E with UI
npx playwright test --ui
# Debug E2E test
npx playwright test --debug
# Update snapshots
npm test -- -u
参考ファイル
references/test-strategy.md- テスト戦略の包括的なフレームワークreferences/framework-comparison.md- フレームワークの詳細な比較references/coverage-patterns.md- カバレッジ最適化の手法references/ci-integration.md- CI/CD パイプラインの設定
対象:テスト戦略 | ユニットテスト | 統合テスト | E2E テスト | カバレッジ | CI/CD | 不安定なテストのデバッグ
併用先:security-auditor(セキュリティテスト) | performance-engineer(負荷テスト) | code-reviewer(テスト品質)