mcpField
P
py-test-quality

py-test-quality

github.com/l-mbテスト・品質5647 回インストール34 週間前に更新
リポジトリ

py-test-quality について

py-test-qualityは、Pythonのテストカバレッジと有効性を評価するスキルです。リファクタリング前にテストの備えを確認する開発者向けで、カバレッジ分析とミューテーションテストにより未テスト箇所やテストが検出できない不具合を探します。

主な機能

  1. 01文と分岐のカバレッジレポート作成
  2. 02未テストのコード経路を特定
  3. 03mutmutによるミューテーションテスト
  4. 04残存ミュータントからテストの不足を分析
  5. 05リファクタリング用のカバレッジ基準を作成
  6. 06CIでカバレッジしきい値を設定

活用シーン

  1. 01リファクタリング前にモジュールのカバレッジを確認
  2. 02未カバーのエラー分岐にテストを追加
  3. 03テストが見逃す境界条件の不具合を発見
  4. 04CIでカバレッジチェックを実行

インストール

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

$npx skills add https://github.com/l-mb/python-refactoring-skills --skill py-test-quality

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

詳細

Python テスト品質分析

カバレッジ分析とミューテーションテストを使用して、テストカバレッジを測定し、テストスイートの有効性を検証します。

目標

  1. コードカバレッジを包括的に測定する
  2. テストされていないコードパスを特定する
  3. テストスイートがバグを検出できることを検証する(ミューテーションテスト)
  4. 高いテストカバレッジによって、安全なリファクタリングを可能にする
  5. カバレッジの推移を継続的に追跡する

必要なツール

[dependency-groups] の dev に追加: "pytest"、"pytest-cov"、"mutmut"、"coverage" 任意: "cosmic-ray"(高度なミューテーションテスト)

  • pytest-cov:コードカバレッジの測定
  • mutmut:ミューテーションテスト。テストがバグを検出できることを検証します
  • cosmic-ray:高度なミューテーションテスト(低速)

権限:最初に py-quality-setup を実行し、必要なすべてのツール権限を .claude/settings.local.json に設定します。

カバレッジ分析

カバレッジの測定
# Run tests with coverage
pytest --cov=. --cov-report=term-missing
pytest --cov=. --cov-report=html  # Generate HTML report
pytest --cov=. --cov-report=term --cov-report=html  # Both

# Coverage with specific targets
pytest --cov=src --cov=lib tests/
pytest --cov=mypackage --cov-branch  # Include branch coverage

# Fail if coverage below threshold
pytest --cov=. --cov-fail-under=80

# Show only uncovered lines
pytest --cov=. --cov-report=term-missing:skip-covered
カバレッジの設定

pyproject.toml に追加します(基本設定については py-quality-setup を参照してください)。

[tool.coverage.run]
source = ["src"]  # Adjust to your source directory
omit = [
    "*/tests/*",
    "*/test_*.py",
    "*/__pycache__/*",
    "*/venv/*",
    "*/.venv/*",
]
branch = true  # Enable branch coverage

[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
    "if TYPE_CHECKING:",
    "@abstractmethod",
]

[tool.coverage.html]
directory = "htmlcov"
カバレッジレポートの解釈
Name                 Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------
src/auth.py            45      5     12      2    87%   23-25, 67
src/database.py        89      0     18      0   100%
src/handlers.py       123     35     28     12    68%   45-78, 99-110
----------------------------------------------------------------
TOTAL                 257     40     58     14    82%

主な指標:

  • Stmts:ステートメントの合計
  • Miss:カバーされていないステートメント
  • Branch:分岐の合計(if/else など)
  • BrPart:一部のみカバーされた分岐(片方のパスはテスト済みだが、もう片方は未テスト)
  • Cover:カバレッジ率
  • Missing:カバーされていない行番号

カバレッジの目標:

  • ≥80%:最低限許容できる水準
  • ≥90%:良好なカバレッジ
  • 100%:理想的(すべてのコードで実現できるとは限りません)

ミューテーションテスト

ミューテーションテストでは、小さな変更(ミューテーション)を加えてテストを実行し、失敗するかどうかを確認することで、テストが実際にバグを検出できることを検証します。

ミューテーションテストの実行
# Using mutmut (easier, faster)
mutmut run                    # Run all mutations
mutmut run --paths-to-mutate=src/  # Specific directory
mutmut results                # Show summary
mutmut show <mutation-id>     # Show specific mutation
mutmut apply <mutation-id>    # Apply mutation to see code change

# Common workflow
mutmut run
mutmut results               # Shows: survived, killed, timeout
mutmut show 1                # Examine first surviving mutation
Mutmut の設定

setup.cfg または pyproject.toml に追加します。

[tool.mutmut]
paths_to_mutate = "src/"
backup = false
runner = "pytest -x --tb=short"
tests_dir = "tests/"
ミューテーション結果の解釈
Legend for output:
🎉 Killed mutants: Tests caught the bug (good!)
⏰ Timeout: Mutation created infinite loop (acceptable)
🤔 Suspicious: Needs investigation
🙁 Survived: Bug not caught by tests (bad!)

ミューテーションスコア:killed / (killed + survived) * 100%

ミューテーションスコアの目標:

  • ≥75%:良好なテスト品質
  • ≥85%:非常に優れたテスト品質
  • 100%:完璧(達成できることはまれです)
生き残ったミューテーションへの対処
# 1. Identify surviving mutations
mutmut results

# 2. Show specific mutation
mutmut show 5

# Example output:
# src/auth.py:23
# -    if user.age >= 18:
# +    if user.age > 18:

# 3. Write test to kill this mutation
def test_user_exactly_18_is_adult():
    user = User(age=18)
    assert is_adult(user) is True  # This would fail with > instead of >=

# 4. Re-run mutmut
mutmut run

# 5. Verify mutation now killed
mutmut results

カバレッジに基づくリファクタリング

基本原則:十分にテストされているコードのみをリファクタリングします。カバレッジが低い場合は、まずテストを書きます。

ワークフロー
1. Run: pytest --cov=. --cov-report=html --cov-fail-under=80
2. If coverage < 80%:
   a. Open htmlcov/index.html
   b. Identify modules with low coverage (red/yellow)
   c. Write tests to increase coverage to ≥80%
   d. Re-run coverage to verify
3. Run: mutmut run (optional but recommended)
4. If mutation score < 75%:
   a. Review surviving mutations
   b. Write tests to kill mutations
   c. Re-run mutmut
5. NOW safe to refactor:
   a. Apply refactoring patterns
   b. Re-run: pytest --cov=. (ensure coverage maintained)
   c. Re-run: mutmut run (ensure mutation score maintained)

リファクタリングとの統合

リファクタリング前のチェックリスト
# 1. Measure baseline coverage
pytest --cov=src/module_to_refactor.py --cov-report=term-missing

# 2. If coverage < 80%, write tests first
# ... write tests ...

# 3. Verify tests are effective
mutmut run --paths-to-mutate=src/module_to_refactor.py

# 4. Now proceed with refactoring
# ... refactor ...

# 5. Verify coverage maintained
pytest --cov=src/module_to_refactor.py --cov-fail-under=80

# 6. Verify tests still effective
mutmut run --paths-to-mutate=src/module_to_refactor.py

CI/CD との統合

CI にカバレッジのしきい値チェックを追加します。

# .github/workflows/test.yml
- name: Run tests with coverage
  run: |
    pytest --cov=. --cov-report=term --cov-report=xml --cov-fail-under=80

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v3
  with:
    file: ./coverage.xml

検証チェックリスト

  • pytest --cov=. --cov-fail-under=80 が成功する
  • カバレッジレポートを確認済み(htmlcov/index.html)
  • 重要なパスがテストでカバーされている
  • 重要なモジュールでミューテーションテストを実行済み(mutmut run)
  • 重要なコードのミューテーションスコアが 75% 以上
  • pyproject.toml にカバレッジ設定がある

例

例:リファクタリング前にカバレッジを向上させる

1. Run: pytest --cov=src/handlers.py --cov-report=html
2. Coverage: 55% (too low to refactor safely)
3. Open htmlcov/handlers_py.html
4. Lines 45-78 not covered (error handling paths)
5. Write tests for error cases:
   - test_handler_invalid_input()
   - test_handler_database_error()
   - test_handler_missing_params()
6. Re-run: pytest --cov=src/handlers.py
7. Coverage now: 85% (safe to refactor)
8. Proceed with complexity reduction refactoring
9. After refactoring: pytest --cov=src/handlers.py --cov-fail-under=85
10. Verify coverage maintained at 85%

例:ミューテーションテストを使ってテストを改善する

1. Run: mutmut run --paths-to-mutate=src/auth.py
2. Results: 15 killed, 3 survived (83% mutation score)
3. Run: mutmut show 5
4. Mutation: Changed >= to > in age check
5. Realize: Missing boundary test for age exactly 18
6. Write: test_user_exactly_18_is_adult()
7. Run: mutmut run --paths-to-mutate=src/auth.py
8. Results: 16 killed, 2 survived (89% mutation score)
9. Repeat for remaining survivors
10. Final: 18 killed, 0 survived (100% mutation score)

例:カバレッジに基づくリファクタリングのセッション

1. Target: Refactor src/payment.py (complexity D, 150 lines)
2. Check coverage: pytest --cov=src/payment.py --cov-report=term-missing
3. Coverage: 92% (good! safe to refactor)
4. Check test quality: mutmut run --paths-to-mutate=src/payment.py
5. Mutation score: 78% (acceptable)
6. Proceed with refactoring:
   - Extract 4 smaller functions
   - Reduce complexity from D to A/B
7. After refactoring:
   - pytest --cov=src/payment.py --cov-fail-under=92 ✓
   - mutmut run --paths-to-mutate=src/payment.py
   - Mutation score: 80% (improved!)
8. Commit changes with confidence

例:CI でカバレッジ追跡を設定する

1. Add to pyproject.toml:
   [tool.coverage.run]
   source = ["src"]
   branch = true

2. Create .github/workflows/test.yml:
   - pytest --cov=. --cov-fail-under=80

3. First run fails: Coverage 67%
4. Write tests to reach 80%
5. CI now passes
6. All future PRs must maintain 80% coverage
7. Consider increasing threshold as coverage improves

関連スキル

  • 前提条件:py-quality-setup(pyproject.toml で pytest-cov を設定する)
  • 活用先:py-security、py-code-health、py-complexity(テストカバレッジに基づく安全なリファクタリング)
  • 適用:py-git-hooks(CI にカバレッジチェックを追加する)

コメント