mcpField
T
test-automation-expert

test-automation-expert

github.com/curiositechテスト・品質257199 回インストール2 日前に更新
リポジトリ

test-automation-expert について

テスト戦略、自動テストの実装、カバレッジ改善、フレークするテストの調査を支援するスキルです。JavaScript/TypeScript と Python の例を交え、単体・統合・E2E テストを扱います。

主な機能

  1. 01テスト戦略の策定
  2. 02単体・統合・E2E テストの作成
  3. 03テストフレームワークの選定
  4. 04カバレッジ向上と不足箇所の特定
  5. 05フレークするテストの調査
  6. 06CI/CD テストパイプラインの設定

活用シーン

  1. 01新規プロジェクトのテスト導入
  2. 02重要なユーザーフローのテスト
  3. 03不安定な自動テストの原因調査
  4. 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.json
references/
ci-integration.md
coverage-patterns.md
framework-comparison.md
test-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 各コンポーネントの契約
E2E10%< 30s 各重要なパス

フレームワークの選択

JavaScript/TypeScript
フレームワーク最適な用途速度設定の複雑さ
VitestVite プロジェクト、最新の ESM最速低
JestReact、実績のあるプロジェクト高速中
PlaywrightE2E、クロスブラウザー該当なし低
CypressE2E、コンポーネントテスト該当なし中
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');
  });
});
不安定なテストの検出と防止

一般的な原因:

  1. 非同期処理での競合状態
  2. 時間に依存するテスト
  3. テスト間で共有される状態
  4. ネットワークの変動
  5. アニメーションやトランジションのタイミング

修正方法:

// ❌ 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(テスト品質)

コメント