本ドキュメントは Claude How To のテスト基盤について説明する。

概要

本プロジェクトは GitHub Actions を使い、すべてのプッシュとプルリクエストで自動的にテストを実行する。テスト内容:

ローカルでテストを実行する

前提条件

# Install uv (fast Python package manager)
pip install uv

# Or on macOS with Homebrew
brew install uv

環境セットアップ

# Clone the repository
git clone YOUR_REPO
cd claude-howto

# Create virtual environment
uv venv

# Activate it
source .venv/bin/activate  # macOS/Linux
# or
.venv\Scripts\activate     # Windows

# Install development dependencies
uv pip install -r requirements-dev.txt

テスト実行

# Run all unit tests
pytest scripts/tests/ -v

# Run tests with coverage
pytest scripts/tests/ -v --cov=scripts --cov-report=html

# Run specific test file
pytest scripts/tests/test_build_epub.py -v

# Run specific test function
pytest scripts/tests/test_build_epub.py::test_function_name -v

# Run tests in watch mode (requires pytest-watch)
ptw scripts/tests/

リンティング実行

# Check code formatting
ruff format --check scripts/

# Auto-fix formatting issues
ruff format scripts/

# Run linter
ruff check scripts/

# Auto-fix linter issues
ruff check --fix scripts/

セキュリティスキャン実行

# Run Bandit security scan
bandit -c pyproject.toml -r scripts/ --exclude scripts/tests/

# Generate JSON report
bandit -c pyproject.toml -r scripts/ --exclude scripts/tests/ -f json -o bandit-report.json

型チェック実行

# Check types with mypy
mypy scripts/ --ignore-missing-imports --no-implicit-optional

GitHub Actions ワークフロー

トリガー

ジョブ

1. ユニットテスト(pytest)

結果: テストが 1 つでも失敗するとワークフロー失敗(クリティカル)

2. コード品質(Ruff)

結果: ノンブロッキング(警告のみ)

3. セキュリティスキャン(Bandit)

結果: ノンブロッキング(警告のみ)

4. 型チェック(mypy)

結果: ノンブロッキング(警告のみ)

5. EPUB ビルド

結果: ビルドが失敗するとワークフロー失敗(クリティカル)

6. サマリ

テストの書き方

テスト構造

テストは scripts/tests/ 配下に test_*.py のような名前で配置する:

# scripts/tests/test_example.py
import pytest
from scripts.example_module import some_function

def test_basic_functionality():
    """Test that some_function works correctly."""
    result = some_function("input")
    assert result == "expected_output"

def test_error_handling():
    """Test that some_function handles errors gracefully."""
    with pytest.raises(ValueError):
        some_function("invalid_input")

@pytest.mark.asyncio
async def test_async_function():
    """Test async functions."""
    result = await async_function()
    assert result is not None

テストのベストプラクティス

フィクスチャ

共通フィクスチャは scripts/tests/conftest.py に定義する:

# Use fixtures in your tests
def test_something(tmp_path):
    """tmp_path fixture provides temporary directory."""
    test_file = tmp_path / "test.txt"
    test_file.write_text("content")
    assert test_file.read_text() == "content"

カバレッジレポート

ローカルカバレッジ

# Generate coverage report
pytest scripts/tests/ --cov=scripts --cov-report=html

# Open the coverage report in your browser
open htmlcov/index.html

カバレッジ目標

pre-commit フック

本プロジェクトは pre-commit フックでコミット前に自動チェックを実行する:

# Install pre-commit hooks
pre-commit install

# Run hooks manually
pre-commit run --all-files

# Skip hooks for a commit (not recommended)
git commit --no-verify

.pre-commit-config.yaml で設定済みのフック:

トラブルシューティング

ローカルでパスするが CI で失敗する

よくある原因:

  1. Python バージョンの違い: CI は 3.10、3.11、3.12 を使う
  2. 依存関係の不足: requirements-dev.txt を更新する
  3. プラットフォーム差異: パス区切り、環境変数
  4. 不安定なテスト: タイミングや順序に依存するテスト

解決策:

# Test with the same Python versions
uv python install 3.10 3.11 3.12

# Test with clean environment
rm -rf .venv
uv venv
uv pip install -r requirements-dev.txt
pytest scripts/tests/

Bandit が誤検知を出す

セキュリティ警告の中には誤検知がある場合もある。pyproject.toml で設定する:

[tool.bandit]
exclude_dirs = ["scripts/tests"]
skips = ["B101"]  # Skip assert_used warning

型チェックが厳しすぎる

特定ファイルで型チェックを緩める:

# Add at the top of file
# type: ignore

# Or for specific lines
some_dynamic_code()  # type: ignore

継続的インテグレーションのベストプラクティス

  1. テストを高速に保つ: 各テストは 1 秒以内で完了する
  2. 外部 API をテストしない: 外部サービスをモックする
  3. 隔離してテストする: 各テストは独立しているべき
  4. 明確なアサートを使う: assert x ではなく assert x == 5
  5. 非同期テストを扱う: @pytest.mark.asyncio を使う
  6. レポートを生成する: カバレッジ、セキュリティ、型チェック

リソース

テストへの貢献

PR を提出する際:

  1. 新機能には テストを書く
  2. ローカルでテストを実行: pytest scripts/tests/ -v
  3. カバレッジを確認: pytest scripts/tests/ --cov=scripts
  4. リンティングを実行: ruff check scripts/
  5. セキュリティスキャン: bandit -r scripts/ --exclude scripts/tests/
  6. テストが変わる場合は ドキュメントを更新

すべての PR にはテストが必要!🧪


テストに関する質問や問題は、GitHub Issue または Discussion を開いてほしい。