CommunityCodierung & Entwicklunggithub.com

crap-analysis

Analysiere Codeabdeckung und CRAP-Scores (Change Risk Anti-Patterns), um risikoreichen Code zu identifizieren. Verwendet das OpenCover-Format mit ReportGenerator für Risiko-Hotspots, die zyklomatische Komplexität und ungetestete Codepfade anzeigen.

Was ist crap-analysis?

Diese Fähigkeit (Skill) ermöglicht die Analyse von Codeabdeckung und CRAP-Scores (Change Risk Anti-Patterns) für .NET-Projekte. Der CRAP-Score, definiert als `Komplexität x (1 - Abdeckung)^2`, kombiniert die zyklomatische Komplexität mit der Testabdeckung, um risikoreichen Code zu identifizieren, der priorisiert getestet oder umgestaltet werden sollte. Der Skill unterstützt die Analyse mittels OpenCover-Format (erforderlich für Komplexitätsmetriken) und ReportGenerator zur Erstellung von HTML-Berichten mit einem „Risk Hotspots“-Abschnitt, der Methoden nach Komplexität sortiert anzeigt. Er bietet eine vollständige Anleitung zur Einrichtung der Coverage-Erfassung mit `coverage.runsettings`, zur Installation von ReportGenerator und zur Integration in CI/CD-Pipelines (GitHub Actions und Azure Pipelines). Der Skill ist besonders nützlich für Entwickler, die die Codequalität vor Änderungen bewerten, Refactoring priorisieren oder Coverage-Schwellenwerte in CI/CD festlegen möchten.

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

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

CRAP-Score-Analyse

Wann dieser Skill verwendet werden sollte

Verwenden Sie diesen Skill, wenn:

  • Sie die Codequalität und Testabdeckung vor Änderungen bewerten möchten
  • Sie risikoreichen Code identifizieren möchten, der umgestaltet oder getestet werden muss
  • Sie die Coverage-Erfassung für ein .NET-Projekt einrichten möchten
  • Sie priorisieren möchten, welcher Code basierend auf Risiko getestet werden soll
  • Sie Coverage-Schwellenwerte für CI/CD-Pipelines festlegen möchten

Was ist CRAP?

CRAP-Score = Komplexität x (1 - Abdeckung)^2

Der CRAP-Score (Change Risk Anti-Patterns) kombiniert die zyklomatische Komplexität mit der Testabdeckung, um risikoreichen Code zu identifizieren.

CRAP-ScoreRisikostufeErforderliche Maßnahme
< 5NiedrigGut getesteter, wartbarer Code
5-30MittelAkzeptabel, aber Komplexität im Auge behalten
> 30HochBenötigt Tests oder Refactoring

Warum CRAP wichtig ist

  • Hohe Komplexität + niedrige Abdeckung = Gefahr: Code, der schwer zu verstehen UND ungetestet ist, ist riskant zu ändern
  • Komplexität allein reicht nicht: Eine komplexe Methode mit 100% Abdeckung ist sicherer als eine einfache Methode mit 0%
  • Fokussiert den Aufwand: Priorisieren Sie das Testen von komplexem Code, nicht von einfachen Gettern/Setzern

CRAP-Score-Beispiele

MethodeKomplexitätAbdeckungBerechnungCRAP
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

Einrichtung der Coverage-Erfassung

coverage.runsettings

Erstellen Sie eine coverage.runsettings-Datei im Stammverzeichnis Ihres Repositorys. Das OpenCover-Format ist für die CRAP-Score-Berechnung erforderlich, da es Metriken zur zyklomatischen Komplexität enthält.

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat code coverage">
        <Configuration>
          <!-- OpenCover-Format enthält zyklomatische Komplexität für CRAP-Scores -->
          <Format>cobertura,opencover</Format>

          <!-- Test- und Benchmark-Assemblys ausschließen -->
          <Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>

          <!-- Generierten Code, obsolete Member und explizite Ausschlüsse ausschließen -->
          <ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>

          <!-- Quellgenerierte Dateien, Blazor-generierten Code und Migrationen ausschließen -->
          <ExcludeByFile>**/obj/**/*,**/*.g.cs,**/*.designer.cs,**/*.razor.g.cs,**/*.razor.css.g.cs,**/Migrations/**/*</ExcludeByFile>

          <!-- Testprojekte ausschließen -->
          <IncludeTestAssembly>false</IncludeTestAssembly>

          <!-- Optimierungsflags -->
          <SingleHit>false</SingleHit>
          <UseSourceLink>true</UseSourceLink>
          <SkipAutoProps>true</SkipAutoProps>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>

Wichtige Konfigurationsoptionen

OptionZweck
FormatMuss opencover für Komplexitätsmetriken enthalten
ExcludeTest-/Benchmark-Assemblys nach Muster ausschließen
ExcludeByAttributeGenerierten, veralteten und explizit ausgeschlossenen Code überspringen (einschließlich ExcludeFromCodeCoverageAttribute)
ExcludeByFileQuellgenerierte Dateien, Blazor-Komponenten und Migrationen überspringen
SkipAutoPropsAuto-Eigenschaften nicht als Verzweigungen zählen

ReportGenerator-Installation

Installieren Sie ReportGenerator als lokales Tool, um HTML-Berichte mit Risiko-Hotspots zu erstellen.

Zu .config/dotnet-tools.json hinzufügen

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-reportgenerator-globaltool": {
      "version": "5.4.5",
      "commands": ["reportgenerator"],
      "rollForward": false
    }
  }
}

Dann wiederherstellen:

dotnet tool restore

Oder global installieren

dotnet tool install --global dotnet-reportgenerator-globaltool

Coverage erfassen

Tests mit Coverage-Erfassung ausführen

# Vorherige Ergebnisse löschen
rm -rf coverage/ TestResults/

# Unit-Tests mit Coverage ausführen
dotnet test tests/MyApp.Tests.Unit \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

# Integrationstests ausführen (optional, erweitert die Coverage)
dotnet test tests/MyApp.Tests.Integration \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

HTML-Bericht erstellen

dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary;MarkdownSummaryGithub"

Berichtstypen

TypBeschreibungAusgabe
HtmlVollständiger interaktiver Berichtcoverage/index.html
TextSummaryReine Textzusammenfassungcoverage/Summary.txt
MarkdownSummaryGithubGitHub-kompatibles Markdowncoverage/SummaryGithub.md
BadgesSVG-Badges für READMEcoverage/badge_*.svg
CoberturaZusammengeführtes Cobertura-XMLcoverage/Cobertura.xml

Den Bericht lesen

Abschnitt „Risk Hotspots“

Der HTML-Bericht enthält einen Abschnitt Risk Hotspots, der Methoden sortiert nach Komplexität anzeigt:

  • Zyklomatische Komplexität: Anzahl der unabhängigen Pfade durch den Code (if/else, switch cases, Schleifen)
  • NPath-Komplexität: Anzahl der azyklischen Ausführungspfade (exponentielles Wachstum bei Verschachtelung)
  • Crap-Score: Berechnet aus Komplexität und Coverage

Ergebnisse interpretieren

Risk Hotspots
─────────────
Method                          Complexity  Coverage  Crap Score
──────────────────────────────────────────────────────────────────
DataImporter.ParseRecord()      54          52%       12.4
AuthService.ValidateToken()     32          0%        32.0   ← HOHES RISIKO
OrderProcessor.Calculate()      28          85%       1.3
UserService.CreateUser()        15          100%      0.0

Aktionspunkte:

  • ValidateToken() hat einen CRAP > 30 bei 0% Coverage – sofort testen oder umgestalten
  • ParseRecord() ist komplex, hat aber eine ordentliche Coverage – akzeptabel
  • CreateUser() und Calculate() sind gut getestet – sicher zu ändern

Coverage-Schwellenwerte

Empfohlene Standards

Coverage-TypZielAktion
Zeilenabdeckung> 80%Gut für die meisten Projekte
Verzweigungsabdeckung> 60%Erfasst bedingte Logik
CRAP-Score< 30Maximal für neuen Code

Schwellenwerte konfigurieren

Erstellen Sie coverage.props in Ihrem Repository:

<Project>
  <PropertyGroup>
    <!-- Coverage-Schwellenwerte für CI-Erzwingung -->
    <CoverageThresholdLine>80</CoverageThresholdLine>
    <CoverageThresholdBranch>60</CoverageThresholdBranch>
  </PropertyGroup>
</Project>

CI/CD-Integration

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'

Kurzreferenz

Einzeiler-Befehle

# Vollständiger Analyse-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"

# Zusammenfassung anzeigen
cat coverage/Summary.txt

# HTML-Bericht öffnen (Linux)
xdg-open coverage/index.html

# HTML-Bericht öffnen (macOS)
open coverage/index.html

# HTML-Bericht öffnen (Windows)
start coverage/index.html

Projektstandards

MetrikNeuer CodeLegacy-Code
Zeilenabdeckung80%+60%+ (schrittweise verbessern)
Verzweigungsabdeckung60%+40%+ (schrittweise verbessern)
Maximaler CRAP30Ausnahmen dokumentieren
Hochrisiko-MethodenMüssen Tests habenTests vor Änderungen hinzufügen

Was ausgeschlossen wird

Die empfohlene coverage.runsettings schließt aus:

MusterGrund
[*.Tests]*Test-Assemblys sind kein Produktionscode
[*.Benchmark]*Benchmark-Projekte
[*.Migrations]*Datenbankmigrationen (generiert)
GeneratedCodeAttributeQuellgeneratoren
CompilerGeneratedAttributeCompiler-generierter Code
ExcludeFromCodeCoverageAttributeExpliziter Entwickler-Opt-out
*.g.cs, *.designer.csGenerierte Dateien
*.razor.g.csGenerierter Blazor-Komponentencode
*.razor.css.g.csGenerierter Code für Blazor-CSS-Isolation
**/Migrations/**/*EF Core-Migrationen (automatisch generiert)
SkipAutoPropsAuto-Eigenschaften (triviale Verzweigungen)

Wann Schwellenwerte aktualisiert werden sollten

Schwellenwerte vorübergehend senken für:

  • Legacy-Codebasen, die modernisiert werden (in README dokumentieren)
  • Generierten Code, der nicht geändert werden kann
  • Drittanbieter-Wrapper-Code

Schwellenwerte niemals senken für:

  • „Es ist zu schwer zu testen“ – stattdessen umgestalten
  • „Wir fügen Tests später hinzu“ – jetzt hinzufügen
  • Neue Funktionen – sollten von Anfang an Standards erfüllen

Zusätzliche Ressourcen

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.

Verwandte Skills