dotnet-testing-code-coverage-analysis
npx skills add https://github.com/kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-code-coverage-analysis
Agent 安装分布
Skill 文档
ç¨å¼ç¢¼è¦èçåææå
é©ç¨æ å¢
ç¶è¢«è¦æ±å·è¡ä»¥ä¸ä»»åæï¼è«ä½¿ç¨æ¤æè½ï¼
- è¨å®èå·è¡ç¨å¼ç¢¼è¦èçåæ
- é ç½® 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/10374467
- ç¯ä¾ç¨å¼ç¢¼ï¼ç¡ï¼æ¬ç« ç¯çºæ¦å¿µèªªæï¼
宿¹æä»¶
- .NET çå®å 測試æä½³åæ³
- 使ç¨ç¨å¼ç¢¼æ¶µèç¯åé²è¡å®å 測試
- dotnet-coverage å·¥å ·
- VS Code Testing
å·¥å ·
ç¸éæè½
unit-test-fundamentals– å®å 測試åºç¤è FIRST ååxunit-project-setup– xUnit å°æ¡è¨å®