🐷

[.NET] コードカバレッジ

2024/05/18に公開

MS 純正

MS 純正の組み込みカバレッジ計測ツールは、Visual Studio 20xx の Enterprise Edition のみで利用できます。

それじゃあ困りますよね、、。
困らない人は、この記事は見なくてかまいません。(笑)

Fine Code Coverage

Fine Code Coverage(VS 拡張機能)は、以下の 3 つのいずれかを使用して、カバレッジ計測を行います。
opencover は古いので除外として、coverlet か MS 標準機能のどちらかを利用します。

  • opencover : archive のため非推奨
  • coverlet
  • RunMsCodeCoverage : 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 に変換するためのツールのインストールが以下になります。

SetupReportGenerator.cmd
REM install formatter only once. (this is preparation. not required on every coverage execution.)
dotnet tool install -g dotnet-reportgenerator-globaltool

RunMsCodeCoverage 利用時は、合わせて以下のコマンドを実行して、dotnet-coverage をインストールしてください。

SetupDotnetCoverage.cmd
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 のデータ コレクターに対応するフレンドリ名です。
RunCoverageWithCoverlet.cmd

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

上記の出力をふまえると、あるべき手順としては以下になります。

  1. カバレッジ実行コマンドの出力を取得する。
  2. その出力から、coverage.cobertura.xml を含む行を取り出す。
  3. その行の前後空白を Trim する。

その解決例として、PowerShell(RunCoverage.ps1)で書くと以下のようになります。

RunCoverageWithCoverlet.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 化する手順が多くなっています。

RunCoverageWithMs.cmd

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 パターンが使えるので、比較的簡単に実現できます。

RunCoverageWithMs.cmd

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