mcpField
P
Pytest Testing

Pytest Testing

github.com/pluginagentmarketplaceテスト・品質6528 回インストール43 週間前に更新
リポジトリ

Pytest Testing について

Python 開発者向けに、pytest を使ったテスト設計から CI 連携までを学ぶスキルです。

主な機能

  1. 01ユニット・統合・機能テスト
  2. 02フィクスチャとテスト準備
  3. 03外部依存のモック
  4. 04テスト駆動開発
  5. 05カバレッジと CI 連携

活用シーン

  1. 01Python プロジェクトの pytest テスト作成
  2. 02モックリクエストによる API テスト
  3. 03カバレッジレポートと CI チェックの追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pluginagentmarketplace/custom-plugin-python --skill pytest-testing

インストール後、Claude Code で/pytest-testingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
config.yaml
references/
GUIDE.md
scripts/
helper.py

詳細

Pytest によるテスト

概要

Python で最も人気のあるテストフレームワーク、pytest を使ってソフトウェアテストを習得しましょう。テスト駆動開発(TDD)を学び、保守しやすいテストを書き、包括的なテスト戦略によってコード品質を確保します。

学習目標

  • pytest でユニットテスト、統合テスト、機能テストを書く
  • テストのセットアップと後処理にフィクスチャを使う
  • 外部依存関係を効果的にモックする
  • テスト駆動開発(TDD)を実践する
  • コードカバレッジを測定し、改善する
  • テストを CI/CD パイプラインに統合する

主なトピック

1. Pytest の基礎
  • テストの検出と命名規則
  • アサーションと比較
  • テストの構成(ファイル、クラス、モジュール)
  • テストの実行(コマンドラインオプション)
  • マーカーとテストの選択
  • パラメーター化テスト

コード例:

# test_calculator.py
import pytest

def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

# Basic test
def test_add():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

# Test exceptions
def test_divide_by_zero():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(10, 0)

# Parametrized test
@pytest.mark.parametrize("a,b,expected", [
    (10, 2, 5),
    (20, 4, 5),
    (100, 10, 10),
    (-10, 2, -5),
])
def test_divide(a, b, expected):
    assert divide(a, b) == expected

# Test with marker
@pytest.mark.slow
def test_complex_operation():
    # This test takes a long time
    result = sum(range(1000000))
    assert result == 499999500000
2. フィクスチャとテストのセットアップ
  • フィクスチャのスコープ(関数、クラス、モジュール、セッション)
  • フィクスチャの依存関係
  • パラメーター化フィクスチャ
  • 組み込みフィクスチャ(tmpdir、capsys、monkeypatch)
  • 共有フィクスチャ用の conftest.py

コード例:

# conftest.py
import pytest
import tempfile
from pathlib import Path

@pytest.fixture
def sample_data():
    """Provide sample data for tests"""
    return {
        'users': [
            {'id': 1, 'name': 'Alice', 'email': '[email protected]'},
            {'id': 2, 'name': 'Bob', 'email': '[email protected]'},
        ]
    }

@pytest.fixture
def temp_file():
    """Create temporary file for testing"""
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
        f.write("Test data")
        temp_path = f.name
    yield temp_path
    # Cleanup
    Path(temp_path).unlink()

@pytest.fixture(scope='module')
def database_connection():
    """Module-scoped database connection"""
    db = DatabaseConnection('test.db')
    db.connect()
    yield db
    db.close()

# test_users.py
def test_user_count(sample_data):
    assert len(sample_data['users']) == 2

def test_user_names(sample_data):
    names = [user['name'] for user in sample_data['users']]
    assert 'Alice' in names
    assert 'Bob' in names

def test_file_operations(temp_file):
    content = Path(temp_file).read_text()
    assert content == "Test data"
3. モックとテストダブル
  • unittest.mock の基礎
  • 関数とメソッドのモック
  • オブジェクトへのパッチ適用
  • モックのアサーション
  • 副作用と戻り値
  • 外部依存関係を使ったテスト

コード例:

# api_client.py
import requests

class APIClient:
    def __init__(self, base_url):
        self.base_url = base_url

    def get_user(self, user_id):
        response = requests.get(f"{self.base_url}/users/{user_id}")
        response.raise_for_status()
        return response.json()

    def create_user(self, user_data):
        response = requests.post(f"{self.base_url}/users", json=user_data)
        response.raise_for_status()
        return response.json()

# test_api_client.py
from unittest.mock import Mock, patch
import pytest

@patch('api_client.requests.get')
def test_get_user(mock_get):
    # Setup mock
    mock_response = Mock()
    mock_response.json.return_value = {'id': 1, 'name': 'Alice'}
    mock_response.raise_for_status.return_value = None
    mock_get.return_value = mock_response

    # Test
    client = APIClient('https://api.example.com')
    user = client.get_user(1)

    # Assertions
    assert user['name'] == 'Alice'
    mock_get.assert_called_once_with('https://api.example.com/users/1')

@patch('api_client.requests.post')
def test_create_user(mock_post):
    # Setup mock
    mock_response = Mock()
    mock_response.json.return_value = {'id': 3, 'name': 'Charlie'}
    mock_post.return_value = mock_response

    # Test
    client = APIClient('https://api.example.com')
    user_data = {'name': 'Charlie', 'email': '[email protected]'}
    result = client.create_user(user_data)

    # Assertions
    assert result['id'] == 3
    mock_post.assert_called_once_with(
        'https://api.example.com/users',
        json=user_data
    )
4. カバレッジと CI/CD の統合
  • pytest-cov でコードカバレッジを測定する
  • カバレッジレポート(ターミナル、HTML、XML)
  • カバレッジのしきい値を設定する
  • GitHub Actions との統合
  • GitLab CI との統合
  • コミット前フック

コード例:

# pytest.ini
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
    --cov=myapp
    --cov-report=html
    --cov-report=term-missing
    --cov-fail-under=80
    -v

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov
      - name: Run tests
        run: |
          pytest --cov=myapp --cov-report=xml
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml

# Command line usage
# Run all tests
pytest

# Run with coverage
pytest --cov=myapp

# Generate HTML coverage report
pytest --cov=myapp --cov-report=html

# Run specific test file
pytest tests/test_api.py

# Run tests with marker
pytest -m slow

# Run tests with verbose output
pytest -v

# Stop on first failure
pytest -x

実践演習

プロジェクト 1:TDD で作る電卓

テスト駆動開発を使って電卓を作成します。

要件:

  • 実装の前にテストを書く
  • 基本的な演算(加算、減算、乗算、除算)
  • エラー処理(ゼロ除算)
  • 科学計算(累乗、平方根、対数)
  • テストカバレッジ 90% 超

主なスキル: TDD のワークフロー、パラメーター化テスト、例外テスト

プロジェクト 2:API テストスイート

REST API の包括的なテストスイートを作成します。

要件:

  • HTTP リクエストをモックする
  • CRUD 操作をテストする
  • エラー処理をテストする
  • 認証をテストする
  • 統合テスト
  • CI/CD パイプラインのセットアップ

主なスキル: モック、フィクスチャ、統合テスト

プロジェクト 3:データベースのテスト

フィクスチャとトランザクションを使ってデータベース操作をテストします。

要件:

  • テスト用データベースのフィクスチャをセットアップする
  • CRUD 操作をテストする
  • トランザクションをロールバックする
  • データ検証
  • パフォーマンステスト
  • カバレッジレポート

主なスキル: データベースフィクスチャ、クリーンアップ、パフォーマンステスト

評価基準

  • 明確で保守しやすいテストを書く
  • フィクスチャを適切に使う
  • 外部依存関係を効果的にモックする
  • コードカバレッジ 80% 超を達成する
  • TDD の原則に従う
  • テストを CI/CD に統合する
  • 意味のあるアサーションを書く

リソース

公式ドキュメント
学習プラットフォーム
ツール

次のステップ

pytest を習得したら、次のトピックに取り組みましょう。

  • プロパティベーステスト - Hypothesis ライブラリ
  • パフォーマンステスト - pytest-benchmark
  • ミューテーションテスト - mutmut
  • 負荷テスト - Locust、pytest-load

コメント