
Unity で開発するときも、他のアプリ開発同様、単体テストを書くことで、 下のようなメリットがあります。
他にもいろいろメリットがあると思いますが、私が感じるメリットはこんな感じです。
Unity で通常、使用される Testing Framework として、UTF(Unity Testing Framework) があります。
UTF は .NET 開発のテストフレームワークである、NUnit をベースにしています。
Unity 2019.2 以降のバージョンでは、UTF を使うには個別パッケージでプロジェクトにインストールしておく必要があります。 ただ、Unity 2020.3 LTS では Unity HUB からプロジェクトを作成するときに選択したテンプレートでは既にインストールされていました。(テンプレートによっては含まれていないものもあるのかも?)
自分の環境で UTF が使えるかを確認してみます。 Unity のメニューから、
-> を選択して
Package Manager のウィンドウのメニューにある、 Unity Registry
を
選択し、ウィンドウ右上の検索バーにTest Framework
と入れると
テストフレームワークが一覧に表示されます。
既に緑のチェックマークが入っている場合は、インストール済みです。 入っていなければ、
ボタンで導入できます。UTF を導入し、テストコードを書くことで、Unity 機能を使った単体テストを実施できます。 いちいち、本番プログラム内にテスト検証コードを含むことなく、 モジュール機能の検証や結果を確認できます。
UTF には 2 つの実行モードがあります。Edit Mode と Play Mode があります。
Unity Editor を編集中のモード(Edit Mode)のままテストを実行できます。 いちいちゲームを実行しなくてもテストを実行できるので便利です。
プロジェクトを Play(実行)したときのゲーム内の状態に併せたテストができます。
UnityTest
属性をつけると、コルーチンとしてテストコードが実行されるようになります。
まずは、テストを実行したり一覧を表示するためのウィンドウを表示してみます。
Unity Editor のメニューから
-> -> を選択すると、Test Runner というウィンドウが開きます。
ボタンを押すと、 現在、プロジェクトで開いている場所にテストコードを作成するためのフォルダが作成されます。
この Tests フォルダの中にはデフォルトで、Tests.asmdef
という拡張子「asmdef」のファイルが出来ています。これはアセンブリ定義ファイルと呼ばれ、Unity の Assembly Definition(略して adf ) と呼ばれる機能の設定を記述しており、ここにテストのアセンブリへの依存関係を定義することで、必要最小限の DLL 呼び出しができます。
アセンブリ定義ファイル(asmdef ファイル)に、テストを実行するために必要なアセンブリへの参照を追加しておく必要があります。 またテストモードが Edit Mode の場合、このフォルダ配下のテストスクリプトが Edit Mode での実行となることを設定しておきます。
Unity Editor 上で asmdef ファイルを選択すると、インスペクタに テストコードから参照するアセンブリや実行プラットフォームの設定ができます。
Editor にチェックを入れておきます。(デフォルトで入っているかと思います)
続いて、テストコードを作成します。
Test Runner のウィンドウ上から
ボタンを押します。
新しく NewTestScript.cs
ファイルが生成されます。名前を適当に変更します。
コードの中身はデフォルトで以下のようになっています。
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class NewTestScript
{
// A Test behaves as an ordinary method
[Test]
public void NewTestScriptSimplePasses()
{
// Use the Assert class to test conditions
}
// A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
// `yield return null;` to skip a frame.
[UnityTest]
public IEnumerator NewTestScriptWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
}
}
[Test]
と [UnityTest]
の Attribute(属性)がついている、
2つのメソッドがあります。
この2つがテストコードとなっており、
Test Runner ウィンドウ上の一覧から確認、実行ができます。
ウィンドウ上で実行したいテストを
-> を 選択すると、テストを実行できます。結果、緑のチェックが表示され、テストが成功したことがわかります。
また、ウィンドウの上部にある
をクリックすれば、すべてのテストコードが実行されます。Unity 関係なく C# 開発で利用できる、通常の NUnit のコードを書いてみます。
参考として、[Test]
Attribute がついたテストを2件作成。
Edit Mode、Play Mode の両方で動かすことができます。
以下のようにテストケースを作ります(先ほどのテストコードを書き換えます)
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class NewTestScript
{
[Test]
public void Test1_Equal() {
Debug.Log("Here is my first Test!");
Assert.That( 5*20, Is.EqualTo(100));
}
[Test]
public void Test2_Equal() {
Debug.Log("Here is my second Test!");
Assert.That(3, Is.InRange(5,10));
}
}
実行すると、Test2_Equal
のテストケースは失敗していることが判りました。
テストコードには以下のような NUnit 3.0 の Assert を使用できます。
例としてはこんな感じ。
Assert.That(100, Is.EqualTo(5 * 20));
数値 100
は 5 * 20
と同じなら検証OK、ということになります。
NUnit 2 の Assert 機能(Classic Model)
NUnit 2 の Assert 機能(Classic Model)を使ったテストの検証コードも引き続き使えますが、新しい NUnit 機能がシンプルにテストを書けることや、新機能は(後方互換のため?)今後、Classic Model での書き方では適用されないため、公式では NUnit 3~ の書き方を推奨しているようです。
NUnit の Classic Model な Assertion の機能一覧はコチラ(nunit.org 公式)。
Unity の Test Runner ウィンドウ上からテストの成功、失敗したときの 状況やデバッグログ出力内容を確認できます。
Test Runner で失敗したケースを選択してみます。
Test2_Equals のテストケースをクリックして確認してみると
Assert.That
が失敗した比較の内容について出力されています。
また、テストケース内で記述した Debug.Log
の内容もここで確認できます。
NUnit3 では基本的に Assert.That
を利用します。
Assert.That( 結果 , 期待される制約 );
よく使う制約(Constraint)の書き方を書いておきます。
記述例 | 例の意味 |
---|---|
Assert.That( a, Is.EqualTo( 2 )); | a が 2 と等しい |
Assert.That( b, Is.InRange( 5,10)); | 5 <= b <= 10 |
Assert.That( new List |
リスト(1,2,3) に 要素 2 が含まれる |
Assert.That( c, Is.Not.EqualTo(3) ); | c が 3 と等しくない |
Assert.That( d, Is.EqualTo(1).Or.EqualTo(2)); | d は 1 OR 2 |
Assert.That( e, Is.GreaterThan(3).And.LessThan(5) | 3 <= e AND e <= 5 |
例えば Edit Mode から別のスクリプト内の機能を確認するとき、 うまくクラスを参照できない(解決できない)場合があります。(Play Mode のテストでも同様)
Assets\NodachiFramework\common\unity_util\Tests\OperateHierarchyTest.cs(6,7): error CS0246: The type or namespace name 'NodachiFramework' could not be found (are you missing a using directive or an assembly reference?)
プロジェクトのアセンブリ定義ファイル(asmdef ファイル)を作っておき テスト用アセンブリ定義ファイルからの参照を設定します。
ゲーム用のAssetフォルダ配下が以下のようなフォルダ構成のとき 「CommonUtil.cs」をテストしたいとします。
Script
+- CommonUtil.cs
Tests
+- EditTest
| +- MyEditTestAssembly.asmdef
| +- SampleEditTest.cs
|
+- PlayTest
+- PlayTestAssembly.asmdef
+- SamplePlayTest.cs
Script の配下には ゲームプロジェクトで使う様々なスクリプトが入っているとし、 Script 直下にアセンブリ定義ファイルを新規作成します。
Unity の Project 上から、Scriptフォルダ
-> -> で新規にアセンブリ定義ファイルを作ることができます。テスト用に作成した PlayTestAssembly.asmdef
のインスペクタから、
Assembly Definition References の ボタンから
先ほど Script 配下に作った Assembly Definition を追加します。
これでテストコードからプロジェクトのScript フォルダ配下のクラスについても参照できるようになりました。
ゲームを実行した時の状態についてテストする流れを確認していきます。
以下のようなプロジェクトの構成があったとします。
Resources
+- Prehabs
+- Bomb
Script
+- BombBehavior.cs
Bomb(爆弾)のプレハブには、スクリプト「BombBehavior.cs」がアタッチされており、 時間がたつと爆発する!という簡単なものを想定しています。 今回は、この BombBehavior.cs の挙動をテストします。
確認用に Tests フォルダをプロジェクト直下に作ります。 (別に場所は自由です)
Resources
+- Prehabs
+- Bomb
Script
+- BombBehavior.cs
Tests
Unity Editor のメニューから
-> -> を選択して、Test Runner というウィンドウを開きます。プロジェクトから Tests フォルダを選択した状態で、
Test Runner のウィンドウを開いたら、
を選択し、 を選択します。
新しく「Tests/Tests」フォルダが出来ますので、リネームして「Tests/PlayTests」に変更しました。
Resources
+- Prehabs
+- Bomb
Script
+- BombBehavior.cs
Tests
+- PlayTests
+- PlayTests.asmdef
Script フォルダ配下の、「BombBehavior.cs」スクリプトの内容をテストしたいので、 テストコードから参照できるように、 Script直下にアセンブリ定義を作成しておきます。
ScriptフォルダEdit Mode、Play Mode のテストコードからの他モジュールへの参照」の項目ご参照)
-> -> で新規にアセンブリ定義ファイルを作っておきます。 (当記事内の「Resources
+- Prehabs
+- Bomb
Script
+- BombBehavior.cs
+- MyGameAssembly.asmdef
Tests
+- PlayTests
+- PlayTests.asmdef
「PlayTests.asmdef」のインスペクタを開き、Script直下の「MyGameAssembly.asmdef」を Assembly Definitiono Reference に追加します。
続いて、PlayTests フォルダの中に、テストコードを書くための「SamplePlayTest.cs」を作成。
Resources
+- Prehabs
+- Bomb
Script
+- BombBehavior.cs
Tests
+- PlayTests
+- PlayTests.asmdef
+- SamplePlayTest.cs
以下のようなコードを書きました。
public class UnityPlayModeTest
{
[UnityTest]
public IEnumerator BombTimer_Success()
{
GameObject bombPrehab = MonoBehaviour.Instantiate (
Resources.Load<GameObject>("Prehabs/Bomb"),
new Vector3( 0.0f, 0.0f, 1.0f),
Quaternion.identity);
BombBehavior bombBehavior = bombPrehab.GetComponent<BombBehavior>();
// (A)まだ爆弾は爆発していない
Assert.That( bombBehavior.isExploded(), Is.EqualTo(false) );
yield return new WaitForSeconds(5.0f);
// (B)5秒だったので爆発している
Assert.That( bombBehavior.isExploded(), Is.EqualTo(true) );
GameObject.Destroy(bombBehavior.gameObject);
yield return null;
}
}
このテストコードのメソッド BombTimer_Success
は、
Prehabs/Bomb
のプレハブをテスト用のシーンに生成して、挙動をチェックするテストコードです。
この Bomb プレハブは3秒で爆発し、isExploded()
メソッドで爆発しているかどうか、 True / False で
確認できます。
テストケースの中では、11行目の(A)で爆発していないことをチェックし、 15秒目の(B)で爆発していることをチェックしています。
これで Edit Mode と同じように、TestRunner 上からテストできました。
実行すると、テスト用のシーンが立ち上がりしばらくすると結果が出ます。
実行時は、テスト用シーンの中で、爆弾(Bomb)プレハブが生成され、 コルーチンとしてテストコードが呼び出さされて、5秒経過した前後の挙動が確認されます。
日付 | 変更概要 |
---|---|
なし |
コメント、ありがとうございます。
ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。
Twitter:@NodachiSoft_jpお名前:以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください
お名前: