C#+VisualStudio+AzureDevOpsで単体テスト（ローカル実行、カバレッジレポート取得、DevOpsでテスト結果参照等）

どんな仕事でも単体テストに関しては大体やること同じなので自分用のリンク集＆メモです。参考サイトさんたちに感謝いたします。

# 使うツール類
- Visual Studio 2022
- Fine Code Coverage
- MSTest
- Coverlet
- ReportGenerator

# 単体テスト基礎

https://learn.microsoft.com/ja-jp/visualstudio/test/getting-started-with-unit-testing?view=vs-2022&tabs=dotnet%2Cmstest

https://learn.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-best-practices

## MSTest
https://learn.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-with-mstest

https://qiita.com/mima_ita/items/55394bcc851eb8b6dc24

## 考え方
基本的にはMicrosoftのBest Practiceに沿えばよいが、その他いくつか。

### ホワイトテストのための直行表作成
Inputパラメータのパターンが多い場合は直行表を作る。レビュワーは直行表ベースでレビューを行う。

https://www.qbook.jp/column/662.html

### カバレッジ
闇雲にコードカバレッジが高いから品質が良いというわけでもない。テストする工数と品質のメリデメを勘案してテストする箇所を決める。
　
Microsoftは目安を出していないが、自作部分（特にビジネスロジック）は適宜DI、モックして本来カバー率100％を目指すべきと考える（色々意見はあるけど）。ただし、テンプレートから作成された部分やリポジトリとか外部ライブラリを呼び出しているだけのものは例外的にカバー率低でも許容する。
　　
個人的に思うのは、テストケース数が多いから、「必要最低限」のテスト数に絞るという記事を見かけることがある（ペアワイズ法とか）が、インプットパターン数が多いことを単体テストをしないことの言い訳にしてはならない。そういう場合は可能な限りリファクタリングするか、直行表を作ったうえでDataRowやヘルパーメソッドとかでテンプレート化し、なるべく単体テストケースをシンプルに量産することを推奨。
　
単体テストが条件網羅とか詳細を確認するのに適しているので、手動でパターンテストをするとなるともっと大変。

目指すべきカバレッジのパターンは直行表を作っていれば自然とC2になるはず。

https://www.qbook.jp/column/932.html


### 適宜コードメトリックスを取得し問題があればリファクタ
複雑なコードはテストも複雑になる。

https://learn.microsoft.com/ja-jp/visualstudio/code-quality/code-metrics-values?view=vs-2022

### 命名規則
    - フォルダ構造：テストプロジェクトもテスト対象プロジェクトとフォルダ構造は同じにする
    　
    - プロジェクト名：テスト対象プロジェクト名.Tests.csproj
    例：MyBankProject.csproj → MyBankProject.Tests.csproj
    　
    - クラス名：テスト対象クラス名＋Tests.cs
    例：MyBankClass.cs → MyBankClassTests.cs
    　
    - メソッド名：テスト対象クラス名_テスト対象メソッド名_When_条件_Then_想定
    例：MyBankClassのCalculateAmountメソッド → MyBankClassTests_CalculateAmount_When_Amount_Not_Zero_Then_Returns_Correct_Amount

### その他
- モックツールについて
モックには、Moqを使いたいところだが、NSubstituteを使う方が当面は安全か。

https://iototsecnews.jp/2023/08/09/popular-open-source-project-moq-criticized-for-quietly-collecting-data/

# ASP.Netアプリのテスト
https://learn.microsoft.com/ja-jp/aspnet/mvc/overview/older-versions-1/unit-testing/creating-unit-tests-for-asp-net-mvc-applications-cs

- ModelのテストはValidatorを利用する

https://qiita.com/SY81517/items/c90a5a78b06ed46c24de#%E3%83%A2%E3%83%87%E3%83%AB%E6%A4%9C%E8%A8%BC%E3%81%AE%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88

- Filterのテスト

https://stackoverflow.com/questions/46162940/how-to-unit-test-actionfilterattribute

# カバレッジを取得する
https://learn.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-code-coverage?tabs=windows

Enterprse Editionを使っている場合。

https://learn.microsoft.com/ja-jp/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested?view=vs-2022&tabs=csharp

Enterprise Editionではない場合、CoverletとReport Generatorを使う。

https://qiita.com/kojimadev/items/84d0a31a94fe6b22b450

上記のCoverletとReport Generatorのレポートをpowershellで出力する。

https://tnakamura.hatenablog.com/entry/2022/05/20/unit-testing-code-coverage

追記：Visual Studio上で見るなら以下のツールが使えると教えていただきました！

https://marketplace.visualstudio.com/items?itemName=FortuneNgwenya.FineCodeCoverage2022

# Git hookでCommit前 (or Push前)に単体テストを実行

https://kinsta.com/jp/blog/git-hooks/

https://www.frankelydiaz.com/blog/net-core-on-windows-how-to-execute-unit-test-before-i-commit

チーム開発をする場合はGit hookスクリプトをチェックインして、それを各開発環境のgit hookディレクトリに配置してもらう。

# DevOpsでテスト結果、カバレッジを発行する

https://learn.microsoft.com/fi-fi/azure/devops/pipelines/test/review-continuous-test-results-after-build?view=azure-devops#tests-tab

https://learn.microsoft.com/ja-jp/azure/devops/pipelines/test/review-code-coverage-results?view=azure-devops

https://josh-ops.com/posts/azure-devops-code-coverage/

```
# ASP.NET Core (.NET Framework)
# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core

trigger:
- develop

pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
- task: NuGetToolInstaller@1

- task: NuGetCommand@2
  inputs:
    restoreSolution: '$(solution)'

- task: VSBuild@1
  inputs:
    solution: '$(solution)'
    msbuildArgs: '/p:SkipInvalidConfigurations=true'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: DotNetCoreCLI@2
  inputs:
    command: 'test'
    projects: '**/*.Tests.csproj'
    arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage"'
    publishTestResults: true

- task: PublishCodeCoverageResults@1
  displayName: 'Publish code coverage'
  inputs:
    codeCoverageTool: Cobertura
    summaryFileLocation: $(Agent.TempDirectory)/*/coverage.cobertura.xml
```

※PublishCodeCoverageResults@1はバージョン@2を使うこと推奨。（調べてないだけです。。。いつか更新したい。）

# DevOpsのBuild ValidationでPull Request時にトリガーしテスト失敗したらマージさせない

https://zenn.dev/nuits_jp/articles/2023-07-02-azure-pipelines-pr-trigger

https://kumar-ashwin-hubert.hashnode.dev/azure-devops-pull-request-build-validation-pipeline-for-net-5-project

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=browser#build-validation

## BuildValidationについて注意点
ちなみに、BuildValidationが正常に実行されるには、pipelineのymlファイルがソースとターゲットブランチで両方に存在する必要がある。

FeatureブランチからdevelopブランチにPRによりマージしたとき、①と②がそろっているとき以下のように2回Pipelineが実行されるらしい。（Copilotに質問しただけでちゃんと動作確認したわけではないので、いつか確認予定。）

①developブランチにTrigger: developに設定したPipelineがある
②developブランチにBuildValidationが設定されている

- 1回目：BuildValidationにより、マージ元ブランチ（Featureブランチ）を自動検知し、それに対してPipeline実行（ここで失敗した場合は2回目は実行されない）
- 2回目：Triggerにより、developブランチの更新を検知し、マージ先ブランチに対してPipeline実行