目次
はじめに
- この一連の記事は
- 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 を直接開いて、
- テストコードを実装する
- 2つ目以降は
- Test Runner ウィンドウからテストを実行する
参考
DIコンテナの導入
DIコンテナは
- 主な利用目的
- レイヤー境界での実装の分離、の解決
- テスタビリティの向上(今回はこっち)
実装の分離
テストを目的とする場合、アプリケーションのプログラムに"テストのため"のコード、都合を混入せずに済み、テストを実行するまで依存関係の解決を遅らせることができる
- DIコンテナは、"それ"だけで複雑な依存関係を解消したり、設計を容易にするための銀の弾丸ではないので使い所は慎重にすべき
- 次の記事は今から18年近く前、DIが発表された翌年2005年の記事
- 長い時間、何度となく同じような悩みを抱えてることが分かる
- www.ulsystems.co.jp
インタフェースは
- 主な目的
- コントラクト(契約)
- 使う側、使われる側がどういうインターフェースでやり取りするかを決める行為
- クラスが何であれ、インターフェースが同じであれば、中身を知らなくても同じコントラクトでインターフェースの機能を利用できる
- 依存性の逆転
- 使う側、使われる側が直接参照関係を結ぶのではなく、間にインターフェースを挟むことで双方がインターフェースに依存するようになる
- このインターフェースを使う側が持つことで依存関係が逆転する
- インタフェースを使われる側が持つとまったく意味がない
- A->B
- A->interface<-B
- 境界はレイヤー境界(Namespaceなど)だったり、アセンブリの境界だったり
- o : A->interface<-|-B
- x : A-|->interface<-B
- o : A->interface<-|-B
- A->B
- コントラクト(契約)
- 単体テストで持つ役割
- 依存性逆転を利用して、テストに都合の良い動きをするモックを参照するように仕向ける
- こうすることでテストしたい機能(クラス、メソッド)に対して、呼び出し側、または機能から呼ばれる側の条件を自由に設定して様々な条件下のテストが容易にできるようになる
Extenject(Zenject)
- AssetStoreからExtenjectを追加する
- Extenject Dependency Injection IOC | Utilities Tools | Unity Asset Store
- git URL で入れると上手く動かないケースがある
- OptionalExtrasは、TestFrameworkのみチェックして追加する
- アプリケーションのasmdefに、
zenject
の参照を追加する - 基本的な使い方
VContainer
- VContainerを追加する
https://github.com/hadashiA/VContainer.git?path=VContainer/Assets/VContainer#1.13.2
- アプリケーションのasmdefに、
VContainer
の参照を追加する - 基本的な使い方
- Inject対象を実装する
- インジェクト方法は4つ
- MonoBehaviour
- コンストラクタ使えないので初期化メソッドを定義してメソッドインジェクション
MonoBehaviour
へのインジェクトは自動ではおこなれないので以下のどれかが必要- Inspectorから
LifetimeScope
に対象のGameObjectを登録する - コードから
RegisterComponent*
で登録する Instantiate
する場面では、IObjectResolver.Instantiate
を代用する
- Inspectorから
LifetimeScope
を継承したコンポーネントで、依存関係を定義する- Hierarchyで
LifetimeScope
を追加する
- Inject対象を実装する
比較の参考
- ZenjectもExtenjectも更新が止まってるらしい(どこが最新?最終??)
- Zenject との比較 | Vコンテナ
- ZenjectからVContainerに移行する際に気をつけること - imog
モックライブラリ導入
- テスト用のモッククラスをテストケースの数だけ定義するのは無理がある
- モックライブラリは動的にモックを生成できるため、テストケース(メソッド)の中で生成、動作を定義してその場で使えるようにしてくれる
Moq
- Unityでもメジャーなライブラリ
- だがしかし、SponsorLinkを内包したことで物議を醸し、ユーザー離れが加速
- Popular open source project Moq criticized for quietly collecting data
- Moq から NSubstitute へ移行する
NSubstitute
Pureな.NETのプロジェクトの方ではMicrosoft Fakesが未だに健在らしい。
これは、Visual Studio Enterprise以上でしか使えない「高級」かつ「重厚」なモックライブラリで、`DateTime.Now` すら書き換えられる強力なやつ。
Microsoft Fakes を使用したテストでのコードの分離 - Visual Studio (Windows) | Microsoft Learn
これは、Visual Studio Enterprise以上でしか使えない「高級」かつ「重厚」なモックライブラリで、`DateTime.Now` すら書き換えられる強力なやつ。
Microsoft Fakes を使用したテストでのコードの分離 - Visual Studio (Windows) | Microsoft Learn