きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × Angularなどの忘備録

dotnet core × Unit Test × コードカバレッジ

Purpose

UnitTestの方針を考える

各種規約について

ディレクトリ構造

本筋とは関係ないが、ソリューションの構造は以下のように定義した。
root直下には、srcと、testsディレクトリを配置。そのほかには、Slnファイルと、Gitignoreファイル、ReadMe.mdファイルを配置した。
アプリケーションの実装プロジェクトは、Srcフォルダの下に配置するようにした。対応するUnitTest用のプロジェクトはtestsフォルダ配下に配置した。
ユニットテストは、テスト対象のクラスごとにテストクラスを作ると一つのテストクラスが大きくなりすぎる傾向があったため、メソッド毎に作成するようにディレクトリを分けた。

root
├─src
│  └─UnitTestSample
│      ├─obj
│      ├─bin
│      ├─Controllers
│      ├─Extensions
│      ├─Interfaces
│      ├─Migrations
│      ├─Models
│      ├─Properties
│      └─Services
└─tests
│   └─NUnitTest
│       ├─obj
│       ├─bin
│       ├─Controllers
│       └─Services
│           ├─DatetimeServiceUnitTest <- UnitTestのテスト対象のクラス専用ディレクトリ
│           └─HeavyWorkOneUnitTest
└─ReadMe.md

テストクラス関連

テストクラスは、メソッド毎に作成する。テスト対象のクラスが以下のように定義されている場合は、DatetimeServiceUnitTestディレクトリを作成し、その中に、GetDateTime.csと、GetDateTimeString.csクラスを作成するという感じにした。

    public class DatetimeService : IDatetimeService
    {
        public DateTime GetDateTime()
        {
            return DateTime.Now;
        }

        public string GetDateTimeString()
        {
            return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
        }
    }

テストメソッド関連

.NET Core と .NET Standard での単体テストのベスト プラクティスによると、テストの名前は、以下の要素があると管理がしやすいとのことだった。

テストの名前は、次の 3 つの部分で構成される必要があります。 - テスト対象のメソッドの名前。 - それがテストされるシナリオ。 - シナリオが呼び出されたときに想定される動作。

メソッドの名前はクラス名になっているので、テストシナリオ_想定の動作を名前にすることでテストケースの管理ができると考えた。
上記の例では、GetDateTime_ReturnsCurrentDateTimeとかにすればいいはず。題材が良くなかったが。

利用するフレームワークについて

3種類のテストフレームワークがあり、迷うところ。 - MSTest - xUnit - NUnit

トレンドとしては、xUnitが優勢。 xUnit.netの視点からの比較はこちらで紹介されている。そこまで込み入ったテストをまだ想定できていないため、これといった決め手はない。
次セクションのコードカバレッジまで見据えると、Unitテストプロジェクトは規定でcoverlet.collectorと統合されているらしいため、とっつきやすい印象がある。※ほかのテストフレームワークでも対応しているため、最初の設定をするかしないか、そのレベルの話。
その他の視点でいえば、テストメソッドに付与しないといけない属性は、それぞれあるが、xUnitに関しては、すべてのPublicクラスに定義されているテストメソッドを認識するため、テストクラスに付与しないといけない属性はない。NUnitでは、TestFixtureをつけないといけない。
とりあえず二大巨頭のNunit、xUnitを使いながら開発にあったほうで進めてみる。

カバレッジについて

.NETのテストと統合するを参考に・・・

coverletの実行

xUnitを選択した場合は、既定で設定されているため、すぐに以下の動作確認が実行できる。その他のフレームワークではCoverletをインストールするところからスタートする。 テストプロジェクトのディレクトリで、以下のコマンドを実行。

dotnet test --collect:"XPlat Code Coverage"

reportgenerator

コードカバレッジの出力結果のディレクトリで、以下のコマンドを実行。

reportgenerator  -reports:.\coverage.cobertura.xml -targetdir:. -reporttypes:Html

CIに向けてまとめてやりたい場合は?

上記のコマンドでは、出力されたコードカバレッジのファイルがGUIDが名前のディレクトリに配置されるため、Reportgeneratorを実行するためには、そのディレクトリ名をさがして、指定する必要があった。これは若干面倒くさいためバッチ化して管理することにした。

$id = [Guid]::NewGuid().ToString()
if (Test-Path .\TestResults\coverage.cobertura.xml ){
    Rename-Item .\TestResults\coverage.cobertura.xml -NewName coverage.cobertura.$id.xml
}
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:Exclude="[xunit*]\*" /p:CoverletOutput="./TestResults/"
reportgenerator "-reports:TestResults\coverage.cobertura.xml" "-targetdir:TestResults\html" -reporttypes:HTML

これをpowershellで実行すれば、TestResultsディレクトリ配下のhtmlディレクトリにコードカバレッジのサマリが出力される。まずは、これを足掛かりにユニットテストの自動化を進めていこうと思う。

参考

今回作ったサンプルソリューション