dotnet-testing-code-coverage-analysis
SKILL.md
程式碼覆蓋率分析指南
適用情境
當被要求執行以下任務時,請使用此技能:
- 設定與執行程式碼覆蓋率分析
- 配置 Coverlet 或其他覆蓋率工具
- 產生與解讀覆蓋率報告
- 在 Visual Studio 或 VS Code 中檢視覆蓋率
- 評估測試完整性與品質
- 結合複雜度指標制定測試策略
Code Coverage 核心概念
定義
程式碼覆蓋率 (Code Coverage) 是一種測量指標,用來統計測試執行時實際執行了多少程式碼。
正確認知
Code Coverage 的實際價值:
- 找出測試盲點:快速識別沒有被測試的程式碼
- 評估測試完整性:檢查重要邏輯是否都有測試
- 輔助重構決策:了解哪些區域需要更多關注
- 增加測試信心:確認關鍵路徑都有被驗證
常見誤解(必須避免)
❌ 錯誤認知:
- 涵蓋率 100% 就沒有 Bug
- 涵蓋率數字越高越好
- 可以用涵蓋率當作 KPI
✅ 正確認知:
- Code Coverage 只是提醒工具,告訴你哪些程式碼沒被測試
- 重點是測試的有效性,不是覆蓋率數字
- 幫助判斷是否需要補充測試案例
- 絕對不應該當作 KPI 使用
⚠️ 警告:當 Code Coverage 被當作 KPI 時,開發者會為了衝數字而寫沒有 Assert 的測試,完全失去了測試的意義。
.NET 專案的覆蓋率工具選擇
1. Visual Studio Enterprise(僅限企業版)
優點:
- 內建整合,無需額外設定
- 完整的 UI 支援
- 即時結果顯示
限制:
- 只有 Enterprise 版本才有此功能
- Professional 和 Community 版本不支援
2. Fine Code Coverage(推薦免費方案)
優點:
- 完全免費
- 整合在 Visual Studio 中
- 即時顯示覆蓋率
- 程式碼編輯器直接標示
安裝方式:
- 開啟 Visual Studio
- 延伸模組 → 管理延伸模組
- 搜尋 "Fine Code Coverage"
- 安裝後重新啟動
必要設定:
- 工具 → 選項 → Fine Code Coverage
- Run (Common) → Enable:設為
True - Editor Colouring Line Highlighting:設為
True
3. .NET CLI 工具
使用場景:
- CI/CD 流程整合
- 命令列自動化
- 跨平台開發
安裝與使用:
# 安裝工具
dotnet tool install -g dotnet-coverage
# 執行測試並產生報告
dotnet-coverage collect dotnet test
# 或使用 Coverlet(推薦)
dotnet test --collect:"XPlat Code Coverage"
4. VS Code 內建測試覆蓋率
優點:
- 跨平台支援
- 內建功能,無需安裝擴充套件
- 整合式測試管理
使用方式:
- 安裝 C# Dev Kit 擴充套件
- 開啟測試總管(燒杯圖示)
- 點選「執行涵蓋範圍測試」
- 查看結果:測試涵蓋範圍視圖、編輯器內顯示、檔案總管顯示
執行覆蓋率分析
方法一:使用 .NET CLI(推薦用於 CI/CD)
# 執行測試並收集覆蓋率
dotnet test --collect:"XPlat Code Coverage"
# 指定輸出格式
dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage
# 產生多種格式報告
dotnet test /p:CollectCoverage=true /p:CoverageReportFormat="cobertura;opencover;json"
方法二:使用 Fine Code Coverage
- 在 Visual Studio 中執行測試
- 檢視 → 其他視窗 → Fine Code Coverage
- 自動顯示覆蓋率報告
方法三:使用 VS Code
- 開啟測試總管
- 點選「執行涵蓋範圍測試」圖示
- 查看覆蓋率結果:
- 測試涵蓋範圍視圖:樹狀結構
- 編輯器內顯示:綠色/紅色標示
- 檔案總管:百分比顯示
設定 Coverlet
在 csproj 中配置
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- 測試框架套件 -->
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<!-- 覆蓋率收集器 -->
<PackageReference Include="coverlet.collector" Version="6.0.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
使用 runsettings 檔案
請參考 templates/runsettings-template.xml 檔案,用於更進階的覆蓋率設定:
- 排除特定檔案或命名空間
- 設定覆蓋率閾值
- 自訂報告格式
使用方式:
dotnet test --settings coverage.runsettings
解讀覆蓋率報告
顏色標示說明
在程式碼編輯器中:
- 綠色:已被測試覆蓋
- 黃色:部分覆蓋(部分分支未測試)
- 紅色:未被覆蓋
覆蓋率指標
-
Line Coverage(行覆蓋率)
- 被執行的程式碼行數 / 總程式碼行數
- 最基本的指標
-
Branch Coverage(分支覆蓋率)
- 被執行的分支數 / 總分支數
- 比行覆蓋率更準確
- 確保 if/else、switch 等所有分支都被測試
-
Method Coverage(方法覆蓋率)
- 被執行的方法數 / 總方法數
報告解讀策略
-
優先處理紅色區域
- 完全沒被測試的程式碼
- 可能是關鍵業務邏輯
-
檢查黃色區域
- 確認所有條件分支都有測試
- 特別注意 if/else、try/catch 等
-
評估必要性
- 簡單的 getter/setter 可能不需要測試
- 自動產生的程式碼可以排除
- 專注於業務邏輯與複雜運算
結合複雜度指標
循環複雜度(Cyclomatic Complexity)
定義: 程式中獨立邏輯路徑的數量
與測試案例的關係:
- 循環複雜度 = 至少需要的測試案例數量
- 每個 if、for、while、case、&&、|| 都會增加複雜度
範例:
public int Max(int[] array)
{
if (array == null || array.Length == 0) // +2 (null 判斷 + 長度判斷)
{
throw new ArgumentException("array must not be empty.");
}
int max = array[0];
for (int i = 1; i < array.Length; i++) // +1 (迴圈)
{
if (array[i] > max) // +1 (條件判斷)
{
max = array[i];
}
}
return max; // +1 (方法本身)
}
// 總複雜度 = 5
測試策略:
循環複雜度為 5,至少需要 5 個測試案例:
- 傳入 null → 測試
array == null - 傳入空陣列 → 測試
array.Length == 0 - 單一元素 → 不進入迴圈
- 最大值在開頭 → 迴圈不更新 max
- 最大值在中間 → 迴圈更新 max
Visual Studio 擴充套件
CodeMaintainability:
- 顯示可維護性指標
- 計算循環複雜度
- 評估程式碼品質
CodeMaid:
- Spade 功能:視覺化程式碼結構
- 顯示每個方法的複雜度
- 幫助識別需要重構的程式碼
改善覆蓋率的策略
1. 漸進式改善
目前覆蓋率 → 識別關鍵模組 → 補充測試 → 提升至目標值 → 持續監控
建議流程:
- 第一階段:覆蓋核心業務邏輯(目標 60-70%)
- 第二階段:補充邊界條件測試(目標 70-80%)
- 第三階段:處理異常情境(目標 80-85%)
- 維持階段:新增功能必須有測試
2. 優先順序排序
高優先級(必須測試):
- 業務邏輯核心
- 金融計算
- 資料驗證
- 權限控制
- 異常處理
中優先級(建議測試):
- 資料轉換
- 格式化邏輯
- 查詢邏輯
低優先級(可選測試):
- 簡單的 getter/setter
- DTO 類別
- 自動產生的程式碼
3. 排除不必要的程式碼
在程式碼或 runsettings 中排除:
// 使用屬性排除
[ExcludeFromCodeCoverage]
public class GeneratedCode
{
// ...
}
實戰建議與最佳實踐
測試案例數量決策
-
基於需求分析
- 列出方法的使用案例
- 識別邊界條件和異常情況
- 考慮業務邏輯的各種情境
-
參考複雜度指標
- 循環複雜度提供測試案例下限
- 高複雜度方法需要更多測試
- 考慮重構降低複雜度
-
平衡覆蓋率與品質
- 不以 100% 覆蓋率為唯一目標
- 專注於關鍵業務邏輯
- 確保測試的實際價值
測試策略
四大測試類型:
- 邊界測試:測試輸入值的上下限
- 異常測試:驗證錯誤處理邏輯
- 主流程測試:覆蓋正常的業務流程
- 條件分支測試:確保所有分支都有測試
持續改善
-
定期檢視報告
- 每次提交前檢查涵蓋率變化
- Pull Request 時審查覆蓋率
-
識別風險區域
- 關注未覆蓋的關鍵程式碼
- 優先處理高複雜度未測試區域
-
漸進式改善
- 逐步提升重要模組的測試覆蓋率
- 新功能必須包含測試
-
團隊協作
- 建立測試標準和流程
- Code Review 包含測試檢查
CI/CD 整合
GitHub Actions 範例
name: Test with Coverage
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '9.0.x'
- name: Run tests with coverage
run: dotnet test --collect:"XPlat Code Coverage"
- name: Generate coverage report
run: |
dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:**/coverage.cobertura.xml -targetdir:coverage -reporttypes:Html
- name: Upload coverage
uses: codecov/codecov-action@v3
Azure DevOps 範例
- task: DotNetCoreCLI@2
displayName: 'Run tests with coverage'
inputs:
command: 'test'
arguments: '--collect:"XPlat Code Coverage"'
publishTestResults: true
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(Agent.TempDirectory)/**/*coverage.cobertura.xml'
常見問題與解決方案
Q1: 覆蓋率顯示 0%?
檢查清單:
- 確認已安裝
coverlet.collector套件 - 檢查 runsettings 設定是否正確
- 確認測試有實際執行
- 查看是否有排除設定過於廣泛
Q2: Visual Studio 看不到覆蓋率?
解決方案:
- Community/Professional 版本:安裝 Fine Code Coverage 擴充套件
- Enterprise 版本:使用內建功能
- 確認已啟用覆蓋率收集
Q3: VS Code 無法顯示覆蓋率?
解決方案:
- 確認已安裝 C# Dev Kit
- 重新執行「執行涵蓋範圍測試」
- 檢查 lcov 檔案是否產生
- 嘗試重新載入視窗
Q4: 如何提升覆蓋率?
策略:
- 識別未覆蓋的關鍵程式碼(紅色區域)
- 補充邊界條件測試
- 測試所有條件分支
- 加入異常情境測試
- 考慮重構過於複雜的方法
範本檔案
請參考同目錄下的範本檔案:
templates/runsettings-template.xml- 覆蓋率設定範本templates/coverage-workflow.md- 完整的工作流程說明
檢查清單
設定程式碼覆蓋率時,請確認以下項目:
- 已安裝
coverlet.collector套件 - 可以執行
dotnet test --collect:"XPlat Code Coverage" - 工具可以正常顯示覆蓋率結果
- 了解覆蓋率數字的真正意義(不是 KPI)
- 已排除不必要的程式碼(如自動產生的程式碼)
- 團隊理解覆蓋率是輔助工具而非目標
- 專注於測試品質而非覆蓋率數字
相關技能
unit-test-fundamentals- 單元測試基礎與 FIRST 原則xunit-project-setup- xUnit 測試專案設定test-naming-conventions- 測試命名規範
核心理念
程式碼覆蓋率是手段,不是目的。
重點不在於數字有多高,而在於:
- 關鍵業務邏輯是否都有測試
- 測試是否真正驗證了預期行為
- 是否能在重構時提供信心
參考資源
原始文章
本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
- Day 06 - Code Coverage 程式碼涵蓋範圍實戰指南
- 鐵人賽文章:https://ithelp.ithome.com.tw/articles/10374355
- 範例程式碼:無(本章節為概念說明)
官方文件
工具
相關技能
unit-test-fundamentals- 單元測試基礎與 FIRST 原則xunit-project-setup- xUnit 專案設定
Weekly Installs
6
Repository
kevintsengtw/dotnet-testing-agent-skillsInstalled on
claude-code5
antigravity4
gemini-cli4
windsurf3
opencode3
codex3