mcpField
C
crap-analysis

crap-analysis

github.com/aarononthewebテスト・品質1.2k567 回インストール今日更新
リポジトリ

crap-analysis について

複雑度とテストカバレッジを組み合わせた CRAP スコアで高リスクなメソッドを特定し、テストやリファクタリングを支援する .NET スキルです。

主な機能

  1. 01CRAP リスクスコアの計算
  2. 02OpenCover 対応カバレッジの収集
  3. 03ReportGenerator レポートの生成
  4. 04複雑度とカバレッジのホットスポット確認
  5. 05カバレッジと CRAP のしきい値設定

活用シーン

  1. 01コード変更前のテスト優先度付け
  2. 02複雑でカバレッジが低いメソッドの特定
  3. 03CI/CD へのカバレッジ検査の追加

インストール

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

$npx skills add https://github.com/aaronontheweb/dotnet-skills --skill crap-analysis

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

詳細

CRAP スコア分析

このスキルを使うタイミング

次の場合にこのスキルを使います:

  • 変更前にコード品質とテストカバレッジを評価する
  • リファクタリングやテストが必要な高リスクコードを特定する
  • .NET プロジェクトでカバレッジ収集を設定する
  • リスクに基づいてテスト対象のコードに優先順位を付ける
  • CI/CD パイプラインのカバレッジしきい値を設定する

CRAP とは?

CRAP スコア = 複雑度 x (1 - カバレッジ)^2

CRAP(変更リスクのアンチパターン)スコアは、循環的複雑度とテストカバレッジを組み合わせて、リスクの高いコードを特定します。

CRAP スコアリスクレベル必要な対応
< 5低十分にテストされ、保守しやすいコード
5-30中許容範囲ですが、複雑度に注意が必要です
> 30高テストまたはリファクタリングが必要です
CRAP が重要な理由
  • 高い複雑度 + 低いカバレッジ = 危険:理解が難しく、テストもされていないコードは、変更時のリスクが高くなります
  • 複雑度だけでは不十分です:100% カバレッジの複雑なメソッドは、カバレッジ 0% の単純なメソッドより安全です
  • 作業に優先順位を付けられます:単純なゲッター/セッターではなく、複雑なコードのテストを優先します
CRAP スコアの例
メソッド複雑度カバレッジ計算CRAP
GetUserId()10%1 x (1 - 0)^21
ParseToken()5452%54 x (1 - 0.52)^212.4
ValidateForm()200%20 x (1 - 0)^220
ProcessOrder()4520%45 x (1 - 0.20)^228.8
ImportData()8010%80 x (1 - 0.10)^264.8

カバレッジ収集の設定

coverage.runsettings

リポジトリのルートに coverage.runsettings ファイルを作成します。複雑度のメトリクスが含まれるため、CRAP スコアの計算には OpenCover 形式が必須です。

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat code coverage">
        <Configuration>
          
          <Format>cobertura,opencover</Format>

          
          <Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>

          
          <ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>

          
          <ExcludeByFile>**/obj/**/*,**/*.g.cs,**/*.designer.cs,**/*.razor.g.cs,**/*.razor.css.g.cs,**/Migrations/**/*</ExcludeByFile>

          
          <IncludeTestAssembly>false</IncludeTestAssembly>

          
          <SingleHit>false</SingleHit>
          <UseSourceLink>true</UseSourceLink>
          <SkipAutoProps>true</SkipAutoProps>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>
主な設定オプション
オプション目的
Format複雑度のメトリクスを取得するには opencover を含める必要があります
Excludeパターンを指定してテスト/ベンチマークのアセンブリを除外します
ExcludeByAttribute生成されたコード、廃止予定のコード、明示的に除外されたコードをスキップします(ExcludeFromCodeCoverageAttribute を含みます)
ExcludeByFileソース生成ファイル、Blazor コンポーネント、マイグレーションをスキップします
SkipAutoProps自動プロパティを分岐としてカウントしないようにします

ReportGenerator のインストール

Risk Hotspots を含む HTML レポートを生成するために、ReportGenerator をローカルツールとしてインストールします。

.config/dotnet-tools.json に追加する
{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-reportgenerator-globaltool": {
      "version": "5.4.5",
      "commands": ["reportgenerator"],
      "rollForward": false
    }
  }
}

その後、復元します:

dotnet tool restore
またはグローバルにインストールする
dotnet tool install --global dotnet-reportgenerator-globaltool

カバレッジの収集

カバレッジを収集しながらテストを実行する
# Clean previous results
rm -rf coverage/ TestResults/

# Run unit tests with coverage
dotnet test tests/MyApp.Tests.Unit \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

# Run integration tests (optional, adds to coverage)
dotnet test tests/MyApp.Tests.Integration \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults
HTML レポートを生成する
dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary;MarkdownSummaryGithub"
レポートの種類
種類説明出力
Htmlインタラクティブな完全レポートcoverage/index.html
TextSummaryプレーンテキストの概要coverage/Summary.txt
MarkdownSummaryGithubGitHub 互換の Markdowncoverage/SummaryGithub.md
BadgesREADME 用の SVG バッジcoverage/badge_*.svg
Cobertura統合された Cobertura XMLcoverage/Cobertura.xml

レポートの読み方

Risk Hotspots セクション

HTML レポートには、複雑度順にメソッドを表示する Risk Hotspots セクションがあります:

  • 循環的複雑度:コード内の独立した経路の数(if/else、switch のケース、ループ)
  • NPath 複雑度:非循環実行経路の数(ネストによって指数関数的に増加します)
  • Crap Score:複雑度とカバレッジに基づいて計算されます
結果の解釈
Risk Hotspots
─────────────
Method                          Complexity  Coverage  Crap Score
──────────────────────────────────────────────────────────────────
DataImporter.ParseRecord()      54          52%       12.4
AuthService.ValidateToken()     32          0%        32.0   ← HIGH RISK
OrderProcessor.Calculate()      28          85%       1.3
UserService.CreateUser()        15          100%      0.0

対応項目:

  • ValidateToken() は CRAP > 30 でカバレッジ 0% です。すぐにテストするか、リファクタリングしてください
  • ParseRecord() は複雑ですが、カバレッジは十分です。許容範囲です
  • CreateUser() と Calculate() は十分にテストされています。安全に変更できます

カバレッジのしきい値

推奨基準
カバレッジの種類目標対応
行カバレッジ> 80%ほとんどのプロジェクトで適切です
分岐カバレッジ> 60%条件分岐のロジックを検出します
CRAP スコア< 30新しいコードの上限です
しきい値の設定

リポジトリに coverage.props を作成します。

<Project>
  <PropertyGroup>
    
    <CoverageThresholdLine>80</CoverageThresholdLine>
    <CoverageThresholdBranch>60</CoverageThresholdBranch>
  </PropertyGroup>
</Project>

CI/CD との統合

GitHub Actions
name: Coverage

on:
  pull_request:
    branches: [main, dev]

jobs:
  coverage:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'

      - name: Restore tools
        run: dotnet tool restore

      - name: Run tests with coverage
        run: |
          dotnet test \
            --settings coverage.runsettings \
            --collect:"XPlat Code Coverage" \
            --results-directory ./TestResults

      - name: Generate report
        run: |
          dotnet reportgenerator \
            -reports:"TestResults/**/coverage.opencover.xml" \
            -targetdir:"coverage" \
            -reporttypes:"Html;MarkdownSummaryGithub;Cobertura"

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

      - name: Add coverage to PR
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: coverage/SummaryGithub.md
Azure Pipelines
- task: DotNetCoreCLI@2
  displayName: 'Run tests with coverage'
  inputs:
    command: 'test'
    arguments: '--settings coverage.runsettings --collect:"XPlat Code Coverage" --results-directory $(Build.SourcesDirectory)/TestResults'

- task: DotNetCoreCLI@2
  displayName: 'Generate coverage report'
  inputs:
    command: 'custom'
    custom: 'reportgenerator'
    arguments: '-reports:"$(Build.SourcesDirectory)/TestResults/**/coverage.opencover.xml" -targetdir:"$(Build.SourcesDirectory)/coverage" -reporttypes:"HtmlInline_AzurePipelines;Cobertura"'

- task: PublishCodeCoverageResults@2
  displayName: 'Publish coverage'
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Build.SourcesDirectory)/coverage/Cobertura.xml'

クイックリファレンス

ワンライナーコマンド
# Full analysis workflow
rm -rf coverage/ TestResults/ && \
dotnet test --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults && \
dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary"

# View summary
cat coverage/Summary.txt

# Open HTML report (Linux)
xdg-open coverage/index.html

# Open HTML report (macOS)
open coverage/index.html

# Open HTML report (Windows)
start coverage/index.html
プロジェクト標準
指標新規コードレガシーコード
行カバレッジ80% 以上60% 以上(段階的に改善)
分岐カバレッジ60% 以上40% 以上(段階的に改善)
CRAP の最大値30例外を文書化
高リスクのメソッドテストが必須変更前にテストを追加

除外対象

推奨される coverage.runsettings では、次の項目を除外します。

パターン理由
[*.Tests]*テストアセンブリは本番コードではありません
[*.Benchmark]*ベンチマークプロジェクト
[*.Migrations]*データベースのマイグレーション(自動生成)
GeneratedCodeAttributeソースジェネレーター
CompilerGeneratedAttributeコンパイラーが生成したコード
ExcludeFromCodeCoverageAttribute開発者による明示的な除外
*.g.cs、*.designer.cs生成ファイル
*.razor.g.csBlazor コンポーネントの生成コード
*.razor.css.g.csBlazor CSS 分離の生成コード
**/Migrations/**/*EF Core のマイグレーション(自動生成)
SkipAutoProps自動プロパティ(単純な分岐)

しきい値を更新する場合

次の場合は、一時的にしきい値を下げます。

  • モダナイズ中のレガシーコードベース(README に記載)
  • 変更できない生成コード
  • サードパーティのラッパーコード

次の場合は、しきい値を下げないでください。

  • 「テストが難しすぎる」— 代わりにリファクタリングします
  • 「後でテストを追加する」— 今すぐ追加します
  • 新機能 — 最初から標準を満たす必要があります

参考資料

コメント