
ゲーム開発では、手動テストはすぐに繰り返しになり、エラーが発生しやすくなります。新しい機能に取り組んだり、バグを修正しようとしたりしているときに、これらの終わりのないテストサイクルの1つに自分がいることに気づいたことはありますか?
コードテストを自動化することで、繰り返しの(しかし重要な)QAタスクに費やす時間を減らし、クリエイティブなゲーム開発にもっと時間を使うことができます。
Unityは、Unity Test Frameworkを使用して、ゲームの自動テストを作成、管理、実行するのに役立ちます。
Unity Test Framework(UTF)を使用すると、EditモードとPlayモードの両方でプロジェクトコードをテストできます。スタンドアロン、iOS、またはAndroidなど、さまざまなプラットフォーム向けにテストコードをターゲットにすることもできます。
UTFは、Package Managerを使用してプロジェクトに追加することでインストールされます。
内部的には、UTFは.NET言語用の有名なオープンソーステストライブラリであるNUnitと統合されています。
UTFで書くことができるテストには、主に2つのカテゴリがあります:編集モードと再生モード。
Edit modeテストはUnityエディターで実行され、エディターとゲームコードの両方にアクセスできます。これにより、カスタムエディター拡張をテストしたり、エディターの設定を変更するためにテストを使用して再生モードに入ることができ、Inspectorの値を調整し、さまざまな設定で自動テストを実行するのに役立ちます。
Play modeテストでは、ランタイムでゲームコードを実行できます。テストは一般的にコルーチンとして実行され、[UnityTest]属性を使用します。これにより、複数のフレームで実行できるコードをテストできます。デフォルトでは、再生モードのテストはエディターで実行されますが、さまざまなターゲットプラットフォーム用のスタンドアロンプレイヤービルドでも実行できます。

この例に従うには、Unity Asset Storeからスターターアセット – 三人称キャラクターコントローラーパッケージをインストールし、新しいプロジェクトにインポートする必要があります。

ウィンドウ > パッケージマネージャーを介してUTFをインストールします。パッケージマネージャーのUnityレジストリでテストフレームワークを検索します。執筆時点での最新バージョンである1.3.3を選択してください。
UTFがインストールされたら、テキストエディタでPackages/manifest.jsonファイルを開き、依存関係の後にtestablesセクションを追加します。
、
"testables": [
"com.unity.inputsystem"
]
ファイルを保存します。これは後で、Unity.InputSystem.TestFrameworkアセンブリを参照してテストおよびプレイヤー入力をエミュレートする必要があるときに役立ちます。
エディターに戻り、新しいバージョンのインストールを許可します。

ウィンドウ > 一般 > テストランナーをクリックして、テストランナーエディタウィンドウを表示します。
このチュートリアルの部分では、再生モードのテストの作成に焦点を当てます。テストランナーウィンドウの「テストアセンブリフォルダーを作成」オプションを使用するのではなく、プロジェクトウィンドウを使用して作成します。
プロジェクトアセットフォルダーのルートがハイライトされている状態で、右クリックして作成 > テスト > テストアセンブリフォルダーを選択します。
テストプロジェクトフォルダーが追加され、Tests.asmdef(アセンブリ定義)ファイルが含まれています。これは、テストがゲームモジュールと依存関係を参照するために必要です。
キャラクターコントローラーコードはテストで参照され、アセンブリ定義も必要です。次に、モジュール間のテストを容易にするために、いくつかのアセンブリ定義と参照を設定します。
Assets/StarterAssets/InputSystem プロジェクトフォルダーを右クリックし、Create > Assembly Definition を選択します。それにわかりやすい名前を付けます。例えば、StarterAssetsInputSystem。
新しい StarterAssetsInputSystem.asmdef ファイルを選択し、Inspectorを使用して、Unity.InputSystem へのAssembly Definition Referenceを追加します。適用をクリックします。
Assets/StarterAssets/ThirdPersonController/Scripts プロジェクトフォルダーを右クリックし、Create > Assembly Definition を選択します。それにわかりやすい名前を付けます。例えば、ThirdPersonControllerMain。
前のアセンブリ定義と同様に、InspectorでThirdPersonControllerMainを開き、次の参照を選択します:
- Unity.InputSystem
- StarterAssetsInputSystem
適用をクリックします。

Input Systemの一部をエミュレートするには、テストでそれを参照する必要があります。さらに、Third Person Controllerコード用に作成するアセンブリでStarterAssets名前空間を参照する必要があります。
InspectorでTests.asmdefを開き、次のアセンブリ定義への参照を追加します:
- UnityEngine.TestRunner
- UnityEditor.TestRunner
- Unity.InputSystem
- Unity.InputSystem.TestFramework
- ThirdPersonControllerMain
適用をクリックします。

最初のテストでは、サードパーソンコントローラーパッケージからメインキャラクターを読み込み、移動させる基本的な内容をカバーします。
新しいプロジェクトをシンプルなテスト環境シーンとキャラクタープレハブリソースで設定することから始めます。
Assets/StarterAssets/ThirdPersonController/Scenes/Playground.unityという名前のシーンを開き、ファイル > 名前を付けて保存メニューを使用してこの新しいパスにコピーを保存します:Assets/Scenes/SimpleTesting.unity
ゲームビューにピンクのマテリアルが表示される場合は、レンダーパイプラインコンバーターを使用して、ビルトインレンダーパイプラインからユニバーサルレンダーパイプライン(URP)にマテリアルをアップグレードします。このアーティクルで簡単な概要を確認してください。
プロジェクトアセットフォルダーにResourcesという名前の新しいフォルダーを作成します。注:フォルダー名「Resources」は、UnityのResources.Load()メソッドを使用できるようにするために重要です。
シーンビューのPlayerArmatureゲームオブジェクトを新しいResourcesフォルダーにドラッグ&ドロップし、プロンプトが表示されたらオリジナルプレハブを作成することを選択します。プレハブアセットの名前をキャラクターに変更します。
これが今後のテストで使用される基本キャラクタープレハブになります。
新しいSimpleTestingシーンからPlayerArmatureゲームオブジェクトを削除し、シーンへの変更を保存します。
初期テストセットアップの最後のステップとして、ファイル > ビルド設定に移動し、シーン/SimpleTestingシーンをビルド設定に追加するために「オープンシーンを追加」を選択します。
プロジェクトアセットフォルダー内のテストフォルダーを選択します。右クリックして作成 > テスト > C#テストスクリプトを選択します。
新しいスクリプト CharacterTests の名前を付けてください。IDEでスクリプトを開いて、詳しく見てください。
初期クラスファイルには、いくつかのテストの基本を示す2つのメソッドスタブが用意されています。
次に、テストが「テストに焦点を当てた」ゲームシーンをロードすることを確認します。これは、あなたが焦点を当てているシステムまたはコンポーネントをテストするために必要な最小限の要素を含むシーンであるべきです。
CharacterTestsクラスを更新して、2つの新しい using ステートメントを追加し、InputTestFixture クラスを実装します:
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class CharacterTests :InputTestFixture
CharacterTestsクラスの先頭に2つのプライベートフィールドを追加します:
GameObject character = Resources.Load("Character");
Keyboard keyboard;
characterフィールドは、ResourcesフォルダーからロードされたCharacterプレハブへの参照を保存します。Keyboardは、InputSystemによって提供されるキーボード入力デバイスへの参照を保持します。
CharacterTestsクラスで独自のSetup()メソッドを提供して、基底InputTestFixtureクラスのメソッドをオーバーライドします:
public override void Setup()
{
SceneManager.LoadScene("Scenes/SimpleTesting");
base.Setup();
keyboard = InputSystem.AddDevice();
var mouse = InputSystem.AddDevice();
Press(mouse.rightButton);
Release(mouse.rightButton);;
}
Setup() メソッドは基本クラスの Setup() メソッドを実行し、その後、テストシーンを読み込み、キーボード入力デバイスを初期化することで独自の CharacterTests クラスを設定します。
マウス入力は、第三者コントローラーがシミュレートされた/仮想キーボードデバイスから入力を受け取るために追加されます。これはほぼ「フォーカスを設定する」アクションのようなものです。
最初のテストでは、Prefab からキャラクターをインスタンス化し、それが null でないことを確認します。次のメソッドをテストクラスに追加してください:
[Test]
public void TestPlayerInstantiation()
{
GameObject characterInstance = GameObject.Instantiate(character, Vector3.zero, Quaternion.identity);
Assert.That(characterInstance, !Is.Null);
}
そこにいる間に、サンプルテンプレートテストメソッドをクリーンアップしたいかもしれません。CharacterTestsSimplePasses と CharacterTestsWithEnumeratorPasses メソッドを削除します。

スクリプトを保存し、エディターの Test Runner ウィンドウに戻ります。TestPlayerInstantiation テストをハイライトし、Run Selected をクリックします。
緑のチェックマークは合格したテストを示します。キャラクターがリソースから読み込まれ、テストシーンにインスタンス化され、その時点で null でないことを確認しました。
このテストには [Test] 注釈が使用されていることに気づいたかもしれませんが、[UnityTest] 注釈は使用されていません。UnityTest 属性は、コルーチンが複数のフレームにわたってテストを実行できるようにします。この場合、キャラクターをインスタンス化し、それが読み込まれたことを確認するだけです。
一般的に、特別な指示を出す必要がある場合、フレームをスキップする必要がある場合、または再生モードで一定の時間待つ必要がある場合を除いて、編集モードではUnityTest属性の代わりにNUnit Test属性を使用するべきです。

次に、前方のコントローラーキーを押し続けることでキャラクターが前に進むことを主張するためにUnityTestを使用します。
以下に提供された新しいテストメソッドをCharacterTestsクラスに追加します。
新しいテストヘルパーメソッドが2つ追加されました。Press()とRelease()です。これらはどちらもInputTestFixture基本クラスによって提供され、InputSystemの制御を押したり離したりすることをエミュレートすることで助けてくれます。
TestPlayerMoves()メソッドは次のことを行います:
キャラクターPrefabからキャラクターのインスタンスを(X:の位置にインスタンス化します。0、Y:0、Z:0)
仮想キーボードの上矢印キーを1秒間押し、その後離します。
さらに1秒待ちます(キャラクターが減速して動かなくなるため)。
キャラクターがZ軸上の1.5ユニットより大きい位置に移動したことを主張します。
ファイルを保存し、Test Runnerに戻り、新しいテストを実行します。

次に、シンプルなプレイヤー健康コンポーネントを追加してカスタムMonoBehaviourスクリプトをテストします。
Assets/StarterAssets/ThirdPersonController/Scriptsの下に新しいスクリプトを作成します。それにPlayerHealthという名前を付けます。
IDEでスクリプトを開き、以下に提供されたコードで内容を置き換えます。
ここに多くの新しいコードが追加されています。要約すると、このスクリプトはプレイヤーキャラクターが落下状態にあるかどうかを判断します。落下状態で地面に一度当たると、キャラクターの健康が10%減少します。
Assets/Resourcesの下にあるキャラクタープレハブを見つけてください。プレハブを開いて、新しいPlayerHealthスクリプトコンポーネントを追加します。
次に、テストシーンを使用して、プレイヤーの健康が崖から落ちた後に減少することを確認します。
[UnityTest]属性を使用して、落下ダメージをテストするPlayモードテストを書くことができます。0.2秒以上落下すると、プレイヤーは0.1fのダメージを受けるべきです(最大健康の10%に相当します)。
SimpleTestingシーンでは、崖に上がるための階段が見えます。これはキャラクターを上にスポーンさせてPlayerHealthスクリプトをテストするためのテストプラットフォームです。
再度CharacterTests.csを開き、新しいテストメソッドTestPlayerFallDamageを追加します:
[UnityTest]
public IEnumerator TestPlayerFallDamage()
{
// テストシーンの高い場所にキャラクターをスポーンさせる
GameObject characterInstance = GameObject.Instantiate(character, new Vector3(0f, 4f, 17.2f), Quaternion.identity);
// PlayerHealthコンポーネントへの参照を取得し、現在の健康が満タン(1f)であることを確認します
var characterHealth = characterInstance.GetComponent();
Assert.That(characterHealth.Health, Is.EqualTo(1f));
// 崖から歩き出し、落下を待ちます
Press(keyboard.upArrowKey);
yield return new WaitForSeconds(0.5f);
Release(keyboard.upArrowKey);
2秒待機するために新しいWaitForSecondsを返します。
// 落下ダメージによって1ヘルスポイントが失われたことを確認します。
Assert.That(characterHealth.Health, Is.EqualTo(0.9f));
}
クラスファイルの最上部にStarterAssets名前空間へのusing参照を追加する必要があります。
using StarterAssets;
上記のテストは、テストで一般的に見られるarrange, act, assert (AAA)パターンに従っています。
Assertセクションは、テスト対象のメソッドのアクションが期待通りに動作することを確認します。
次に、シンプルなプレイヤー健康コンポーネントを追加してカスタムMonoBehaviourスクリプトをテストします。
Assets/StarterAssets/ThirdPersonController/Scriptsの下に新しいスクリプトを作成します。それにPlayerHealthという名前を付けます。
IDEでスクリプトを開き、以下に提供されたコードで内容を置き換えます。
ここに多くの新しいコードが追加されています。要約すると、このスクリプトはプレイヤーキャラクターが落下状態にあるかどうかを判断します。落下状態で地面に一度当たると、キャラクターの健康が10%減少します。
Assets/Resourcesの下にあるキャラクタープレハブを見つけてください。プレハブを開いて、新しいPlayerHealthスクリプトコンポーネントを追加します。
次に、テストシーンを使用して、プレイヤーの健康が崖から落ちた後に減少することを確認します。
[UnityTest]属性を使用して、落下ダメージをテストするPlayモードテストを書くことができます。0.2秒以上落下すると、プレイヤーは0.1fのダメージを受けるべきです(最大健康の10%に相当します)。
SimpleTestingシーンでは、崖に上がるための階段が見えます。これはキャラクターを上にスポーンさせてPlayerHealthスクリプトをテストするためのテストプラットフォームです。
再度CharacterTests.csを開き、新しいテストメソッドTestPlayerFallDamageを追加します:
[UnityTest]
public IEnumerator TestPlayerFallDamage()
{
// テストシーンの高い場所にキャラクターをスポーンさせる
GameObject characterInstance = GameObject.Instantiate(character, new Vector3(0f, 4f, 17.2f), Quaternion.identity);
// PlayerHealthコンポーネントへの参照を取得し、現在の健康が満タン(1f)であることを確認します
var characterHealth = characterInstance.GetComponent();
Assert.That(characterHealth.Health, Is.EqualTo(1f));
// 崖から歩き出し、落下を待ちます
Press(keyboard.upArrowKey);
yield return new WaitForSeconds(0.5f);
Release(keyboard.upArrowKey);
2秒待機するために新しいWaitForSecondsを返します。
// 落下ダメージによって1ヘルスポイントが失われたことを確認します。
Assert.That(characterHealth.Health, Is.EqualTo(0.9f));
}
クラスファイルの最上部にStarterAssets名前空間へのusing参照を追加する必要があります。
using StarterAssets;
上記のテストは、テストで一般的に見られるarrange, act, assert (AAA)パターンに従っています。
単体テストメソッドのArrangeセクションは、オブジェクトを初期化し、テスト対象のメソッドに渡されるデータの値を設定します。
Actセクションは、整えられたパラメータでテスト対象のメソッドを呼び出します。この場合、テスト対象のメソッドの呼び出しは、プレイヤーが落下後に地面に衝突する際の物理的インタラクションによって処理されます。
Assertセクションは、テスト対象のメソッドのアクションが期待通りに動作することを確認します。

エディターに戻り、新しいテストを実行します。再生モードで実行すると、キャラクターが端から歩き出し、落下し(落下を分類するための0.2秒のしきい値を超え)、地面に衝突した後にダメージを受けるのが見えます。
テストは、コードの変更が機能を壊さないことを確認する目的だけでなく、設定を調整する際に開発者がゲームの他の側面について考える手助けとして、ドキュメントや指針としても機能します。

前述のように、デフォルトではテストランナーで再生モードのテストを実行すると、Unityエディターを使用して再生モードで実行されます。スタンドアロンプレイヤーでも実行するように変更できます。
テストランナーウィンドウのRun Locationドロップダウン選択を使用して、スタンドアロンプレイヤービルドでテストを実行するように切り替えます。
テストスイートの構築を開始したら、次のステップはビルドが完了した後に自動的にテストを実行することです。ビルド後に実行される自動ユニットおよびインテグレーションテストは、回帰やバグをできるだけ早くキャッチするのに役立ちます。これらは、リモート自動ビルドシステムの一部として実行することもできます。
テスト実行結果をカスタム形式でキャプチャして、結果をより広いオーディエンスと共有できるようにしたい場合がよくあります。Unityエディターの外でテスト結果をキャプチャするには、ビルドと実行プロセスを分割する必要があります。
TestsプロジェクトフォルダーにSetupPlaymodeTestPlayerという名前の新しいスクリプトを作成します。
SetupPlaymodeTestPlayerクラスは、ITestPlayerBuildModifierインターフェースを実装します。これを使用して、ビルドのプレイヤーオプションを受け取り、それを変更できるModifyOptionsメソッドにオーバーライドして「フック」します。
using System.IO;
using UnityEditor;
using UnityEditor.TestTools;
[アセンブリ:TestPlayerBuildModifier(typeof(SetupPlaymodeTestPlayer))]
public class SetupPlaymodeTestPlayer :ITestPlayerBuildModifier
{
public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions)
{
playerOptions.options &= ~(BuildOptions.AutoRunPlayer | BuildOptions.ConnectToHost);
var buildLocation = Path.GetFullPath("TestPlayers");
var fileName = Path.GetFileName(playerOptions.locationPathName);
if (!string.IsNullOrEmpty(fileName))
buildLocation = Path.Combine(buildLocation, fileName);
playerOptions.locationPathName = buildLocation;
return playerOptions;
}
}
このカスタムプレイヤービルドモディファイアスクリプトは、テストがプレイモードで実行されるときに次のことを行います(実行場所:プレイヤー上):
ビルドされたプレイヤーの自動実行を無効にし、実行中のホストに接続しようとするプレイヤーオプションをスキップします。
ビルドパスの場所をプロジェクト内の専用パス(TestPlayers)に変更します。
これが完了すると、ビルドが完了するたびにTestPlayersフォルダーに配置されることが期待できます。これでビルドの変更が完了し、ビルドと実行のリンクが切れます。
次に、結果報告を実装します。これにより、テスト結果をカスタム場所に書き出し、自動レポート生成と公開の準備が整います。
TestsプロジェクトフォルダーにResultSerializerという名前の新しいスクリプトを作成します(以下に提供)。このクラスは、TestRunCallbackへのアセンブリ参照を使用し、ITestRunCallbackインターフェースを実装します。
ITestRunCallbackのこの実装には、テスト結果をtestresults.xmlという名前のXMLファイルに書き出すためのテストを持つプレイヤービルドを設定するRunFinishedメソッドが含まれています。

SetupPlaymodeTestPlayer.csとResultSerializer.csが組み合わさることで、ビルドと実行プロセスが分割されます。テストを実行すると、プレイヤープラットフォームのApplication.persistentDataPath locationにあるtestresults.xmlに結果が出力されます。
これらのフッククラスのいくつかの型を使用するには、Tests.asmdefへの追加の参照を追加する必要があります。UnityEditor.UI.EditorTestsアセンブリ定義参照を追加するように更新します。
プレイヤーでテストを実行すると、TestPlayersフォルダー内のプロジェクトの下にプレイヤービルド出力が生成され、Application.persistentDataPathの場所にtestresults.xmlファイルが生成されます。

Unity Test Framework course
Test Frameworkパッケージには、Unityでのテストについて学ぶためのサンプル演習を特徴とするa testing courseが含まれています。パッケージマネージャーを使用してコースのプロジェクトファイルを取得してください。
Package Manager > Packagesを使用します:Unity レジストリ > テストフレームワーク、サンプルのドロップダウンリストを見つけて、コースの演習をインポートします。
演習はプロジェクトにインポートされ、Assets/Samples/Test Frameworkの下に配置されます。各サンプルには、作業するための演習フォルダーと、進行に合わせて自分の作業と比較するためのソリューションが含まれています。
QA あなたのコードを UTF
この Unite Copenhagen トーク は UTF についての詳細を説明し、テストカスタマイズの他の興味深いユースケースを提供します。何が可能かを確認するために、ぜひチェックしてください。
Unity でのデバッグ
Unity でのデバッグワークフローを加速するための記事:
- Microsoft Visual Studio 2022
- Microsoft Visual Studio Code
高度な技術的電子書籍
Unity は、プロの開発者がゲームコードを最適化するのを助けるための高度なガイドをいくつか提供しています。C# スタイルガイドを作成:クリーンなコードを作成するためのアドバイスを業界の専門家から集めた、チームがクリーンで読みやすく、スケーラブルなコードベースを開発するのを助けるためのコードスタイルガイドを作成します。
ユーザーに人気のある別のガイドは、Unity での生産性を向上させるための70+のヒントです。それは、Unity 2020 LTS での日常的な集約ワークフローを改善するための時間を節約するヒントが満載で、経験豊富な開発者が見逃しているかもしれないヒントも含まれています。
ドキュメント
最新の TestRunner API をさらに探求し、他の UTF カスタム属性について学び、UTF ドキュメント にフックするさらなるライフサイクルを発見します。
Unity の高度な電子書籍と記事は、Unity ベストプラクティスハブ で見つけることができます。