Communityコーディング&開発github.com

crap-analysis

コードカバレッジとCRAP(Change Risk Anti-Patterns)スコアを分析して、リスクの高いコードを特定します。OpenCover形式とReportGeneratorのリスクホットスポット機能を使用し、循環的複雑度と未テストコードパスを可視化します。

crap-analysis とは?

このスキルは、.NETプロジェクトにおけるコード品質の評価とリスク特定に特化しています。CRAPスコア(循環的複雑度×(1-カバレッジ)^2)を計算し、テストが不十分で複雑なコードを「リスクホットスポット」として強調表示します。ReportGeneratorツールを活用し、OpenCover形式(循環的複雑度を含む)でカバレッジデータを収集、HTMLレポートやMarkdownサマリーを生成します。CI/CDパイプライン(GitHub Actions、Azure Pipelines)への統合もサポートしており、プルリクエスト内で直接リスクを可視化できます。このスキルにより、開発者は「変更リスクの高いコード」に優先的にテストを追加したりリファクタリングを行ったりすることで、効率的にコードベースの健全性を向上させることができます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/crap-analysis

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

CRAPスコア分析

このスキルを使用するタイミング

次のような場面で使用します:

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

CRAPとは?

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

CRAP(Change Risk Anti-Patterns)スコアは、循環的複雑度とテストカバレッジを組み合わせて、リスクの高いコードを特定します。

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>
          <!-- OpenCover形式にはCRAPスコア用の循環的複雑度が含まれます -->
          <Format>cobertura,opencover</Format>

          <!-- テストとベンチマークアセンブリを除外 -->
          <Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>

          <!-- 生成コード、廃止メンバー、明示的除外を除外 -->
          <ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>

          <!-- ソース生成ファイル、Blazor生成コード、マイグレーションを除外 -->
          <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のインストール

リスクホットスポットを含む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

カバレッジの収集

カバレッジ付きでテストを実行

# 以前の結果をクリーンアップ
rm -rf coverage/ TestResults/

# 単体テストをカバレッジ付きで実行
dotnet test tests/MyApp.Tests.Unit \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

# 統合テストを実行(オプション、カバレッジに追加)
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互換マークダウンcoverage/SummaryGithub.md
BadgesREADME用SVGバッジcoverage/badge_*.svg
CoberturaマージされたCobertura XMLcoverage/Cobertura.xml

レポートの読み方

リスクホットスポットセクション

HTMLレポートには、複雑度でソートされたメソッドを示すリスクホットスポットセクションが含まれます:

  • 循環的複雑度:コード内の独立したパスの数(if/else、switchケース、ループ)
  • NPath複雑度:非巡回実行パスの数(ネストによる指数関数的増加)
  • CRAPスコア:複雑度とカバレッジから計算

結果の解釈

リスクホットスポット
─────────────
メソッド                          複雑度  カバレッジ  CRAPスコア
──────────────────────────────────────────────────────────────────
DataImporter.ParseRecord()      54          52%       12.4
AuthService.ValidateToken()     32          0%        32.0   ← 高リスク
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>
    <!-- CI適用のためのカバレッジしきい値 -->
    <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: 'カバレッジ付きでテストを実行'
  inputs:
    command: 'test'
    arguments: '--settings coverage.runsettings --collect:"XPlat Code Coverage" --results-directory $(Build.SourcesDirectory)/TestResults'

- task: DotNetCoreCLI@2
  displayName: 'カバレッジレポートを生成'
  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: 'カバレッジを公開'
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Build.SourcesDirectory)/coverage/Cobertura.xml'

クイックリファレンス

ワンライナーコマンド

# 完全な分析ワークフロー
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"

# サマリーを表示
cat coverage/Summary.txt

# HTMLレポートを開く(Linux)
xdg-open coverage/index.html

# HTMLレポートを開く(macOS)
open coverage/index.html

# HTMLレポートを開く(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に文書化)
  • 変更できない生成コード
  • サードパーティラッパーコード

以下の場合、決してしきい値を下げない:

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

追加リソース

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

marketplace-publishing

Workflow for publishing skills and agents to the dotnet-skills Claude Code marketplace. Covers adding new content, updating plugin.json, validation, and release tagging.

関連スキル