【Unity】効率よくアセットバリデーションを作成するためのTestRunnerの活用法

**地味に時間のかかる**アセットバリデーション開発。本記事ではUnityが無料で提供する**TestRunner**を使えば効率よく作れるのではないか？という自分的にも挑戦的な記事になっています。

## 準備

1. Test Frameworkのインストール
2. バリデーションするアセットの用意

### 1.Test Frameworkのインストール

TestRunnerを使用するために、PackageManagerから`Test Framework`をインストールしておきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/3a0fc5b9-b89b-ec98-7d55-1d9e483751b9.png" width=500 />

コンパイルが成功したら`Window > General > Test Runner > Test Runner`でTest Runnerウィンドウを表示しておきます。

### 2.バリデーションするアセットの用意
今回は200個のScriptableObject(1001.asset~1200.asset)がバリデーション対象です。
格納先はどこでも良いですが、`Assets/AssetBundlesフォルダ`配下に追加します。(フォルダ名に制限はありません)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/e70a0ac8-02f5-cc30-5826-fe1908aedf7f.png" width=220 />

これで準備は完了です。

## バリデーション処理の流れ

1. `System.IO.Directory.GetFilesメソッド`でアセットパスの配列を取得
1. `AssetDatabase.LoadAssetAtPathメソッド`でアセットを１つずつロード
1. ロードしたアセットをバリデーション処理に通す


```cs
Assert.IsNotNull(obj, "Invalid obj is null.");
```
上のサンプルコードのようにバリデーションに引っかかったら(失敗したら)、その理由を`Assertの第２引数`で出力します。




## 実際のコード

- TestRunnerから呼ばれるエントリーポイント
- バリデーション処理を記述したメソッド

上記の2つの構成になっています。

#### TestRunnerから呼ばれるエントリーポイント

```AssetTest.cs
/// <summary>
/// TestRunnerから呼ばれるエントリーポイント
/// </summary>
[Test]
public void AssetValidate()
{
    // Assertで処理を止めないようにする
    Assert.raiseExceptions = false;

    var assetDirectoryPath = "Assets/AssetBundles/";
    var filePaths = Directory.GetFiles(assetDirectoryPath, "*.asset");

    foreach (var path in filePaths)
    {
        Validate(path);
    }
}
```

#### バリデーション処理を記述したメソッド

```AssetTest.cs
/// <summary>
/// 各ファイルごとに実行するバリデーション処理
/// </summary>
/// <param name="path">ファイルパス</param>
/// <returns>falseなら失敗 / trueなら成功</returns>
private void Validate(string path)
{
    var fileName = Path.GetFileName(path);
    // アセットをロード
    var asset = AssetDatabase.LoadAssetAtPath<QuestAsset>(path);

    // アセット存在テスト
    Assert.IsNotNull(asset, $"{fileName} => asset is null");

    // ID未記入テスト
    Assert.IsFalse(string.IsNullOrEmpty(asset.id),
        $"{fileName} => ID is null or empty");

    // 命名規則テスト
    Assert.IsFalse(asset.id.IndexOf("A", StringComparison.Ordinal) != 0,
        $"{fileName} => The naming conventions are different. : {asset.id}");
}
```
- アセット存在テスト
- ID未記入テスト
- 命名規則テスト

3種類のバリデーションを用意しました。
Assertの各メソッドの第２引数は失敗時に出力されます。
失敗時の理由を丁寧に書くことで、その後の修正作業の効率が上がります。

## TestRunnerから実行

`Run All`または`Run Selected`をクリックしてTestRunnerを実行します。

![validate2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/14f4b9f8-7666-6c49-90f6-f2266be79e99.gif)
失敗したテストに関しては、事前に書いておいた失敗の理由が出力されます。



## TestRunnerを使うメリット

- `[Test]`アトリビュートを定義するだけで**簡単にTestRunnerウィンドウにテストを追加**でき、簡単にテストを実行することが出来る
- バリデーションをTestRunnerに共通化しておくと**後からジョインしたメンバーが把握しやすい**
- TestRunner自体がPackageなので**簡単にインストール可**
- **テストをコマンドラインから実行できる**のでワークフローが組みやすい(GithubActionsなど)

個人的には上記のようなメリットがあると考えています。

## Assertを使用するときの注意点

TestRunnerでよく使用される`Assertメソッド`をバリデーション処理側に使用すると、デフォルト状態では**テストの失敗時に後続処理が止まります。**

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/eeaca586-462b-9a94-ce9d-c9aaace22e30.png" width=400 />

これではテスト結果の全貌が見えなくなり**開発効率が低下**しますので、以下の1行を追加すると良いです。

```cs
Assert.raiseExceptions = false;
```
こうするとテストに失敗しても処理は継続されます。

## まとめ

TestRunnerを使って簡単なアセットバリデーションを作ってみました。
成功・失敗(+失敗の理由)を**ログ出力する程度のバリデーションであれば、記述するコード量を減らす[^1]ことができ、開発の効率化観点でTestRunnerは有用**だと思いました。

TestRunnerはバッチモードで実行できますので、例えばジェンキンスでバリデーションを走らせて結果をSlack通知させるという活用が可能です。


今回は簡易的なバリデーションでしたが、実践では複雑な実装になります。
**いかにバリデーションをし易くするか**ということを考えて設計・実装することが大事になります。
これはTestRunnerを使用するしないに関わらず必要な視点です。

[^1]: EditorWindowの記述やテスト判定のView処理を書く工数は下げられそうという意味


## 環境

- Unity2019.4.4f1
- Test Framework 1.1.14


今回のサンプル全ソースは以下にアップしています。
https://github.com/baobao/unity-test-runner-asset-test-shortcode