[.NET] コードカバレッジ
MS 純正
MS 純正の組み込みカバレッジ計測ツールは、Visual Studio 20xx の Enterprise Edition のみで利用できます。
それじゃあ困りますよね、、。
困らない人は、この記事は見なくてかまいません。(笑)
Fine Code Coverage
Fine Code Coverage(VS 拡張機能)は、以下の 3 つのいずれかを使用して、カバレッジ計測を行います。
opencover は古いので除外として、coverlet か MS 標準機能のどちらかを利用します。
coverlet 利用時
coverlet で計測する場合は、UnitTest のプロジェクトに coverlet.collector を入れてください。
詳細は、 Coverlet integration with VSTest でも確認できます。
MS 提供のカバレッジ機能利用時
オプション|Fine Code Coverage において、RunMsCodeCoverage を有効にしてください。
GUI 上での実行
表示|その他のウィンドウ から、「Fine Code Coverage」 を選択して表示します。
その後、Debug モードで単体テストを実行すると、カバレッジが表示されます。
CI/CD with Fine Code Coverage
CI/CD を考えると、コマンドラインからカバレッジ実行したい。
ソリューション名:BankLibrary.sln というライブラリソリューションの例として、コマンドラインを考えてみます。このソリューションには、下記のように単体テストプロジェクト(TestBankLibrary)が含まれています。
[source]
└ [BankLibrary]
├ BankLibrary.sln
├ [BankLibrary]/BankLibrary.csproj
└ [TestBankLibrary]/TestBankLibrary.csproj
以降特に明記しない限り、ソリューションのディレクトリ(source/BankLibrary)でコマンド実行するものとします。
準備
カバレッジの結果は、動的な GUID フォルダ以下に XML ファイルとして出力されます。
XML のままだと理解づらいので、HTML に変換します。
XML を HTML に変換するためのツールのインストールが以下になります。
REM install formatter only once. (this is preparation. not required on every coverage execution.)
dotnet tool install -g dotnet-reportgenerator-globaltool
RunMsCodeCoverage 利用時は、合わせて以下のコマンドを実行して、dotnet-coverage をインストールしてください。
REM install formatter only once. (this is preparation. not required on every coverage execution.)
dotnet tool install --global dotnet-coverage
CLI 実行 with coverlet
単体テストにコードカバレッジを使用する - .NET | Microsoft Learn によると、以下のように記載されています。
- "XPlat Code Coverage" 引数は、Coverlet のデータ コレクターに対応するフレンドリ名です。
REM 1. run below command on solution folder. ( result format is XML. )
dotnet test --collect "XPlat Code Coverage"
REM 2. convert result XML to HTML
REM `e7b101c2-0705-4787-8c4a-9b7a3f21990c` is random guid. so you should overwrite `guid` part, before run below command.
reportgenerator -reports:".\TestBankLibrary\TestResults\e7b101c2-0705-4787-8c4a-9b7a3f21990c\coverage.cobertura.xml" -targetdir:"CoverageReport" -reporttypes:Html
上記例のコメントにも書いていますが、結果 XML のパスが動的に生成される GUID フォルダ以下になっています。
つまり、出力先が固定ではないので、HTML 変換までを自動化できないという問題が残っています。
CLI 実行 with coverlet(発展系)
動的な GUID フォルダを含む結果 XML 出力パスを正しく取得して、HTML 化するまでの自動化の手順が必要です。
ちなみにカバレッジ実行(dotnet test --collect)した際の出力は以下のようになります。
dotnet test --collect "XPlat Code Coverage"
復元対象のプロジェクトを決定しています...
復元対象のすべてのプロジェクトは最新です。
BankLibrary -> C:\{anywhere}\source\BankLibrary\BankLibrary\bin\Debug\net8.0\BankLibrary.dll
TestBankLibrary -> C:\{anywhere}\source\BankLibrary\TestBankLibrary\bin\Debug\net8.0\TestBankLibrary.dll (.NETCoreApp,Version=v8.0) のテスト実行
Microsoft (R) Test Execution Command Line Tool Version 17.9.0 (x64)
Copyright (c) Microsoft Corporation. All rights reserved.テスト実行を開始しています。お待ちください...
合計 1 個のテスト ファイルが指定されたパターンと一致しました。成功! -失敗: 0、合格: 24、スキップ: 0、合計: 24、期間: 55 ms - TestBankLibrary.dll (net8.0)
添付ファイル:
C:\{anywhere}\source\BankLibrary\TestBankLibrary\TestResults\189cd4ec-4872-4f47-ba92-593aeed5f3fa\coverage.cobertura.xml
上記の出力をふまえると、あるべき手順としては以下になります。
- カバレッジ実行コマンドの出力を取得する。
- その出力から、coverage.cobertura.xml を含む行を取り出す。
- その行の前後空白を Trim する。
その解決例として、PowerShell(RunCoverage.ps1)で書くと以下のようになります。
# 1) Run coverage
$TestOutput = dotnet test --collect "XPlat Code Coverage"
# 2) Select result XML Path from console output.
# cf. Join-String cmdlet come from PowerShell 6.2. You should use newer version of PowerShell.
$TestReports = $TestOutput | Select-String coverage.cobertura.xml | ForEach-Object { $_.Line.Trim() } | Join-String -Separator ';'
# 3) Then, convert XML to HTML.
# You can see index.html in `CoverageReport` folder.
reportgenerator "-reports:$TestReports" -targetdir:"CoverageReport" -reporttypes:Html
CLI 実行 with RunMsCodeCoverage
MS 提供のカバレッジ機能利用時もほぼ Coverlet と同じような流れになります。
一点違うのは、カバレッジ出力が cobertura バイナリ形式になっているので、いったんそれを XML 化する手順が多くなっています。
REM 1. run below command on solution folder. ( result format is XML. )
dotnet test --collect "Code Coverage"
REM 2. coverage result is cobertura binary format. so convert it to XML.
REM `d8d6071a-94dd-42be-9080-f9c130537d58` is random guid. so you should overwrite `guid` part, before run below command. you must change output .coverage filename also.
dotnet-coverage merge -o merged.cobertura.xml --remove-input-files -f cobertura .\TestBankLibrary\TestResults\d8d6071a-94dd-42be-9080-f9c130537d58\YourAccount_MachineName_2024-05-19.11_17_11.coverage
REM 3. convert result XML to HTML
reportgenerator -reports:".\merged.cobertura.xml" -targetdir:"CoverageReport" -reporttypes:Html
CLI 実行 with RunMsCodeCoverage(発展系)
こちらも coverlet 同様に、動的な GUID フォルダを含む結果 XML 出力パスを正しく取得して、HTML 化するまでの自動化の手順が必要です。
ちなみにカバレッジ実行(dotnet test --collect)した際の出力は以下のようになります。
Determining projects to restore...
復元対象のすべてのプロジェクトは最新です。
BankLibrary -> C:\{anywhere}\source\BankLibrary\BankLibrary\bin\Debug\net8.0\BankLibrary.dll
TestBankLibrary -> C:\{anywhere}\source\BankLibrary\TestBankLibrary\bin\Debug\net8.0\TestBankLibrary.dll
C:\{anywhere}\source\BankLibrary\TestBankLibrary\bin\Debug\net8.0\TestBankLibrary.dll (.NETCoreApp,Version=v8.0) のテスト実行
Microsoft (R) Test Execution Command Line Tool Version 17.9.0 (x64)
Copyright (c) Microsoft Corporation. All rights reserved.テスト実行を開始しています。お待ちください...
合計 1 個のテスト ファイルが指定されたパターンと一致しました。成功! -失敗: 0、合格: 24、スキップ: 0、合計: 24、期間: 185 ms - TestBankLibrary.dll (net8.0)
添付ファイル:
C:\{anywhere}\source\BankLibrary\TestBankLibrary\TestResults\d8d6071a-94dd-42be-9080-f9c130537d58\YourAccount_MachineName_2024-05-19.11_17_11.coverage
dotnet-coverage で glob パターンが使えるので、比較的簡単に実現できます。
REM 1. run below command on solution folder. ( result format is XML. )
dotnet test --collect "Code Coverage"
REM 2. coverage result is cobertura binary format. so convert it to XML.
dotnet-coverage merge -o merged.cobertura.xml --remove-input-files -f cobertura .\TestBankLibrary\**\*.coverage
REM 3. convert result XML to HTML
reportgenerator -reports:".\merged.cobertura.xml" -targetdir:"CoverageReport" -reporttypes:Html
最後に
MS 提供のカバレッジ機能 と coverlet の 2 択なら、当初は前者でよいのではないか思いました。
ただ、私が試した 2024/5 時点においてですが、Line Coverage はどちらも正しく取れたものの、Branch Coverage に関しては、MS 提供のカバレッジ機能だと一部正しく計測できませんでした。
なので、どちらか迷ったら、現時点だと coverlet をお勧めしたいと思います。
Discussion