目次
はじめに
- この一連の記事は
- UnityのUnity Test Frameworkを使ったテストに関して調べたメモ書きに補足を足したもの
- Unityのテスト、およびDIコンテナ、モックライブラリの基本的な使い方やそれぞれの役割など入門レベルの解説
- ライブラリのリファレンス的な使い方などについては公式や他の記事参照
- 実機テストやCIは含まない
- TDDに関しては考慮しない
シリーズの目次
- Unity Unit Test(単体テスト)入門
- Unity Unit Test(単体テスト)入門 - Unity Test Framework(この記事)
- Unity Unit Test(単体テスト)入門 - Extenject(Zenject)
- Unity Unit Test(単体テスト)入門 - VContainer
- Unity Unit Test(単体テスト)入門 - Moq
- Unity Unit Test(単体テスト)入門 - NSubstitute
- Unity Unit Test(単体テスト)入門 - 番外編 UniRx
環境
- Unity 2021.3.29f1
- Package
- Test Framework 1.3.8
- com.unity.test-framework / 1.3.8
- Extenject 9.2.0
- VContainer 1.13.2
https://github.com/hadashiA/VContainer.git?path=VContainer/Assets/VContainer#1.13.2
- Moq
- 4.7.99 (Extenject内蔵)
- 4.20.69 (NuGet)
- NSubstitute
- v2.0.3.0 for .Net v4.5 (Extenject内蔵)
- 5.0.0 (NuGet)
https://github.com/neuecc/UniRx.git?path=Assets/Plugins/UniRx/Scripts
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
- Test Framework 1.3.8
Unity Test Framework
手順
- Test Framework 入ってなければ追加する
- 1.3から非同期使えるのでバージョンを更新する
- Package Managerから
- by url : com.unity.test-framework@1.3
- by name : com.unity.test-framework/1.3.9
- Test Runner ウィンドウを開く
- テストのアセンブリを定義する
- PlayMode、EditModeそれぞれAssebly Folderを作成
- アプリのアセンブリを定義する
- アセンブリ定義を作成する
- テストのアセンブリ定義にアプリのアセンブリ定義の参照を追加する
- ※
Assembly-CSharp.dll
を直接参照しない- Workflow: How to create a Play Mode test | Test Framework | 1.3.9
Enable playmode tests for all assemblies
はエディタから直接触れなくなっている- 一応、ProjectSettings.asset を直接開いて、
playModeTestRunnerEnabled
を1にすれば使える - ビルドする前に0に戻す必要がある
- Test Runnerのタブを右クリックすると、無効化するために再起動を求められる、再起動後0に戻ってる
- 一応、ProjectSettings.asset を直接開いて、
- テストコードを実装する
- Test Runner ウィンドウからテストを実行する
参考
補足
- テストモード
- EditMode
- エディタ上で実行される
- EditModeのテストからPlayModeの開始と終了を呼ぶこともできる(
EnterPlayMode
/ExitPlayMode
)- 専用のSceneを作成して再生するが、元のテストはエディタ上で実行されてるので
WaitForSeconds
は使えないまま
- 専用のSceneを作成して再生するが、元のテストはエディタ上で実行されてるので
- PlayMode
- テスト専用のSceneが作成され、エディタのPlay(再生)で実行される
- ターゲットプラットフォーム上でもテストできる
- ただし、UnityTest属性はWSAをサポートしていない
- Edit Mode vs. Play Mode tests | Test Framework | 1.3.9
- EditMode
テストタイプ
- Test属性
- フレームをまたがない通常のNUnitテスト
[Test] public void Test属性のTest() { Assert.That(1 < 10); }
- UnityTest属性
- EditMode
- エディタのアップデートループ
EditorApplication.update
でテストを実行する yield return null
でフレームを進める
- エディタのアップデートループ
- PlayMode
- Sceneの中でコルーチンとしてテストを実行する
yield return
は通常のコルーチンと同様WaitForSeconds
が使える
- EditMode
[UnityTest] public IEnumerator UnityTest属性のTest() { Assert.That(1, Is.LessThan(10)); yield return null; Assert.That(2, Is.LessThan(10)); yield return null; Assert.That(3, Is.LessThan(10)); }
- Test属性
非同期テスト
- バージョン1.3以降
- EditMode/PlayMode両方で使える
- Test属性のテストメソッドで
async Task
が使える - メソッド内ではTask、UniTaskが使え、DelayやDelayFrameなどフレームをまたぐ処理も可能
[Test] public async Task 非同期TaskのTest() { await Task.Delay(2000); Assert.That(true); } [Test] public async Task 非同期UniTaskのTest() { await UniTask.Delay(2000); await UniTask.DelayFrame(1000); Assert.That(true); }
- EditModeでは
EditorApplication.update
で、PlayModeではUpdate
で、タスクが完了するのをチェックしている- PlayMode & Test属性でUnityTest属性とほぼ同じ動き
- Unity Test Framework v1.3で非同期テスト - やらなイカ?
- Unity Test Frameworkの非同期テストで できること/ できないこと - やらなイカ?
- Async tests | Test Framework | 1.3.9
- バージョン1.3より前
- UnityTest属性で、
UniTask.ToCoroutin
が使える
[UnityTest] public IEnumerator 古いUniTaskコルーチンを使ったTest() => UniTask.ToCoroutine(async () => { await UniTask.Delay(2000); await UniTask.DelayFrame(1000); Assert.That(true); });
- UnityTest属性で、
- バージョン1.3以降
Assert
テストの前後処理
[OneTimeSetUp] public void OneTimeSetUp() => Debug.Log("OneTimeSetUp"); [OneTimeTearDown] public void OneTimeTearDown() => Debug.Log("OneTimeTearDown"); [SetUp] public void Setup() => Debug.Log("Setup"); [TearDown] public void TearDown() => Debug.Log("TearDown"); [UnitySetUp] public IEnumerator UnitySetUp() { Debug.Log("UnitySetUp"); yield return null; } [UnityTearDown] public IEnumerator UnityTearDown() { Debug.Log("UnityTearDown"); yield return null; } [Test] public void TestCase1() => Debug.Log("TestCase1-Test"); [UnityTest] public IEnumerator TestCase2() { Debug.Log("TestCase2-UnityTest"); yield return null; }
- 呼び出し順
- OneTimeSetUp
- UnitySetUp
- Setup
- TestCase1-Test
- TearDown
- UnityTearDown
- UnitySetUp
- Setup
- TestCase2-UnityTest
- TearDown
- UnityTearDown
- OneTimeTearDown
- 【Unity】Unity Test Runner(Test Framework)でテストの前後処理を書く方法まとめ - LIGHT11
- 呼び出し順
- Sceneをロードする
- PlayModeでのみ可能
- 対象のSceneをBuild Settingsでビルドの対象に追加して、
SceneManager.LoadScene("SceneName")
[UnityTest] public IEnumerator SceneをロードするTest() { var name = "SceneLoadTest"; LogAssert.Expect(LogType.Log, $"Hello from {name}"); SceneManager.LoadScene(name); yield return null; }
MEMO
- Assembly Definition File
- asmdef
Define Constraints
は、テストで使用するシンボルの定義UNITY_INCLUDE_TESTS
があることで、テストがビルド時に含まれないようになっている- 【Unity】UTFで「UNITY_INCLUDE_TESTS」があると通常のビルドにはテストコードが含まれないようになっているらしい - はなちるのマイノート
Assembly Deffinition References
は、プロジェクト内のasmdefの参照Assembly References
は、プロジェクト内のdllの参照Include Platforms
でEditorのみにチェックがついてればEditModeという扱い
- ディレクトリが入れ子になっていても、asmdefが定義されている場合は別アセンブリとなる
- ディレクトリ構造は関係ない、参照しないと見えない
- 例)ModuleAにModuleBの参照を追加しないと、ModuleBに含まれているコードは使えない
- ModuleA.asmdef
- ModuleBフォルダ
- ModuleB.asmdef
- ModuleBフォルダ
- ModuleA.asmdef
- 例)以下ではMainModuleにModelsの参照が必要
- patial classはアセンブリをまたがって定義できない
- Unity Assembly Definition 完全に理解した - Qiita
- asmdef
- SerializeReference
- 【Unity】SerializeReferenceをちゃんと理解する - LIGHT11
- SerializeReferenceは非UnityEngine.Objectのための機能
SerializeReferenceアトリビュートを付けたInterface型にUnityEngine.Objectを代入しようとすると以下のようなエラーが出力されます。 Fields with [SerializeReference] cannot serialize objects that derive from Unity.Object
- SerializeReferenceは非UnityEngine.Objectのための機能
- 【Unity】【エディタ拡張】SerializeReferenceでシリアライズする型の派生型をInspectorから選択できるようにする - LIGHT11
- 【Unity】SerializeReferenceをちゃんと理解する - LIGHT11
- プライベートメンバーを参照する
UNITY_INCLUDE_TESTS
を使うと、ビルドに含まれないがエディタやテストで利用できるコードが作れる- 通常のクラス定義
public partial class Sword : ISword { private string _material; private string _author; public string Name => $"{_material}の剣"; public Sword(string material, string author) { _material = material; _author = author; Debug.Log($"{Name} made by {_author}"); // エディタやテストからは呼べるが、通常はビルドする時にエラーになる //InitializeTest(); } }
partial
を使ってプライベートメンバーを公開する- Testsフォルダの中にpartial クラスを作成
- partialクラスの定義
#if UNITY_INCLUDE_TESTS using UnityEngine; namespace UnityTest.Models { public partial class Sword { public string Material => _material; public string Author => _author; private void InitializeTest() { Debug.Log("patial class for testing."); } } } #endif
参考
- Unity使いは全員Unity Test Runnerを使え!爆速のトライ&エラー環境だぞ! - Qiita
- Unityでちゃんとテストを書きたい人のためのまとめ - Qiita
- Unity Test Runnerの使い方を理解する - Qiita
- 【Unity】Unity Test Runner(Test Framework)の使い方を総まとめ - インストールから自動化まで - LIGHT11
- 「Unityテストを完全に理解した」の動画とスライドが公開 - テラシュールブログ
- 【Unity】Test Runner で自動テストを試してみようとしたら CS0246 エラーが出て困ってしまった | パパコーダー
- UnityTestRunnerでUniTaskを使おう! - Qiita
- Unity EditTestでasync/awaitが使いたい - Qiita
- Unity BatchModeでテストを実行する - Qiita