Azure DevOps Server のパイプラインでテストが実行されないことがある件への対処

# 概要

Azure DevOps Server（2020.0.1 + Patch 1）で作成したパイプライン（Azure Pipline、YAML形式）で、Visual Studio 2019では動作していたテストが実行されない（テスト結果が常に0件となる）ことがありましたので、原因と対策を記載します。

# (1) 現象

Azure DevOps Server でテストを含む.NET プロジェクトのパイプラインを実行すると、テスト結果が常に0件となる。

**ログ(VSTest)内のメッセージ**

```
テスト セレクター: テスト アセンブリ
テスト アセンブリ: **\*test*.dll,!**\*TestAdapter.dll,!**\obj\**
テスト フィルターの条件: null
検索フォルダー: C:\agent\_work\21\s
実行設定ファイル: C:\agent\_work\21\s
<省略>
テストの実行で、別のフレームワークとプラットフォームのバージョン用にビルドされた DLL が検出されました。以下の DLL は、現在の設定と一致しません。現在のフレームワーク: .NETFramework,Version=v4.0、プラットフォーム: X86。
<省略>
合計 72 個のテスト ファイルが指定されたパターンと一致しました。
'C:\agent\_work\21\s\TestResults\Administrator_<TEST_SERVER>_2021-01-02_03_04_05.trx' を公開するための結果が見つかりませんでした。
```

💡本来はテストファイル（テスト対象ライブラリのdll）は1個だが、大量のdllが検出されている

## 環境

|環境|バージョン|
|--|--|
|Azure DevOps Server|2020.0.1 + Patch 1|
|Visual Studio|2019 Version 16.8.4|
|構成|.NET デスクトップ|
|テストフレームワーク|NUnit|

# (2) 原因

テスト対象ライブラリ（テストプロジェクトのアセンブリ）を指定するフィルターの既定値が広範囲であり、テスト対象ライブラリ以外も指定されていました。その結果フレームワークとプラットフォームを誤検知し、正常にテストできなくなっていました。

# (3) 詳細

後述の「再現手順」で作成される自動テスト関連のタスクは以下の部分となります。

```yaml
- task: VSTest@2
  inputs:
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'
```

しかし、[ここ](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/test/vstest?view=azure-devops)に記載された既定値、およびログを見ると実際には以下の指定がされています。

```yaml
- task: VSTest@2
  inputs:
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'
    testSelector: testAssemblies
    testAssemblyVer2: |
      **\*test*.dll
      !**\*TestAdapter.dll
      !**\obj\**
```

この`**\*test*.dll`があいまいすぎるため、`Microsoft.TestPlatform.CommunicationUtilities.dll`なども対象と誤判定していました。

<details><summary>再現手順</summary>
<div>

以下の構成でプロジェクトを作成

|プロジェクト名|フレームワーク|
|--|--|
|SampleLib|.NET Standard 2.0|
|SampleLib.Tests|.NET Core 3.1|

1. [接続]Azure Repos Gitを選択
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/97700bd9-d4d8-9eeb-05f7-b183aac4f506.png)

2. [選択]対象のリポジトリを選択

3. [構成].NET デスクトップを選択
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/7f5cde94-3360-5eba-615a-0566ab58305b.png)
   
4. 次のYAMLが作成されるがパイプラインを実行してもテスト結果は常に0件となる。

   ```yaml
   # .NET Desktop
   # Build and run tests for .NET Desktop or Windows classic desktop solutions.
   # Add steps that publish symbols, save build artifacts, and more:
   # https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net

   trigger:
   - master

   pool:
     name: 'Default'

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

   steps:
   - task: NuGetToolInstaller@1

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

   - task: VSBuild@1
     inputs:
       solution: '$(solution)'
       platform: '$(buildPlatform)'
       configuration: '$(buildConfiguration)'

   - task: VSTest@2
     inputs:
       platform: '$(buildPlatform)'
       configuration: '$(buildConfiguration)'
   ```
</div>
</details>



# (4) 対策

テストタスクのフィルターを明記し、対象を限定します。

今回の例では以下を追記し、`**\*.Tests.dll`のdll（今回の例：`bin\Release\netcoreapp3.1\SampleLib.Tests.dll`）のみを対象とすることで解決しました。

```yaml
    testSelector: testAssemblies
    testAssemblyVer2: |
      **\*.Tests.dll
      !**\*TestAdapter.dll
      !**\obj\**
```

最終的には以下の形となりました。

```diff_yaml
  - task: VSTest@2
    inputs:
      platform: '$(buildPlatform)'
      configuration: '$(buildConfiguration)'
+     testSelector: testAssemblies
+     testAssemblyVer2: |
+       **\*.Tests.dll
+       !**\*TestAdapter.dll
+       !**\obj\**
```

**実行ログ**

```
テスト セレクター: テスト アセンブリ
テスト アセンブリ: **\*.Tests.dll,!**\*TestAdapter.dll,!**\obj\**
テスト フィルターの条件: null
検索フォルダー: C:\agent\_work\21\s
実行設定ファイル: C:\agent\_work\21\s
<省略>
合計 1 個のテスト ファイルが指定されたパターンと一致しました。
<省略>
NUnit Adapter 3.16.1.0: Test execution started
<省略>
テストの実行に成功しました。
<省略>
##[section]Async Command Start: テスト結果を公開する
テスト結果をテストの実行 '97' に公開しています
テスト結果の残り: 15。テストの実行 ID: 97
Published Test Run : http://TEST_SERVER:8080/tfs/DefaultCollection/TestProject/_TestManagement/Runs?runId=97&_a=runCharts
##[section]Async Command End: テスト結果を公開する
```

---

# 参考

- https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/test/vstest?view=azure-devops
- https://aonasuzutsuki.hatenablog.jp/entry/2020/12/03/140531
  - この例ではNUnit.ConsoleRunnerを使用することで解決している。