最終更新:2019 年 12 月(読み終わるまでの時間:6 分)

Unity Test Framework を使用してゲームコードをテストする

このページで学ぶ内容:Unity Test Framework(UTF)を使用してプロジェクトの QA を実行する方法についてのヒント。UTF は品質保証のために最も重要なツールの 1 つです。エディターとサポートされているプラットフォームの両方で自動化されたテストを実行できます。また、Unity ユーザーなら誰でもご利用いただけます。  

新しい Test Runner API は、UTF により優れた柔軟性と拡張性をもたらし、テストに関わるほとんどのニーズを満たします。Unity パッケージマネージャーから入手できるため、バグ修正プログラムや新しい更新プログラムを迅速に届けることができます。これは、UTF のソースコードにローカルでアクセスできることも意味します。ソースコードを確認し、デバッグ時にステップ実行して、それを変更できます。  

全体像を確認するには、Unity のツール/テスト開発者である Richard Fine と Christian Warnecke による Unite Copenhagen の UTF に関するセッションをご覧ください。

Unity Test Framework を使用してゲームコードをテストする

Unity Test Framework を使ってみよう

Unity Test Framework(UTF)が初めての方は、初心者向けのドキュメントをお読みください。簡単に言うと、UTF を使用すると Unity ユーザーが編集モードと再生モードのほか、スタンドアロン、Android、iOS などのターゲットプラットフォーム上でコードをテストできます。

UTF では、.Net 言語用のオープンソースのユニットテストライブラリである NUnit ライブラリの Unity インテグレーションが使用されます。NUnit の詳細については、NUnit の公式ウェブサイトNUnit に関する GitHub のドキュメントを参照してください。 

初めての方は、こちらのブログ記事が参考になります。

Unity のパフォーマンスベンチマーキング - 導入ガイド

Unity Test Runner でテスト駆動開発を試す

Test Runner API の概要

Test Runner API(以下の API)を使用して、あらゆるスクリプトからテストをプログラムで実行できます。編集モードか再生モード、またはその両方で実行されるテストの一覧を、実行することなく取得できます。登録/登録解除のコールバックを、各テストの開始と終了およびテストサイクル内の各レベル(全テストアセンブリ、個々のテストフィクスチャごと、テストクラスおよびテストごと)に接続できます。 

各テストの開始時点で、これから実行するテストルートに関する情報が得られます。テストが終了すると、テスト結果が表示されます。 

UTF を Unity エディターの再生モードで実行するのに加えて、新しいカスタマイズポイントによりターゲットデバイス上で実行できます。これはプレイヤーをビルドする前に呼び出されます。プレイヤービルドオプションを変更して、たとえばテストの実行設定を変更したり、ビルド場所を指定したりできます。

TestRunnerAPI.cs (C#)
void Execute(ExecutionSettings executionSettings);
void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks;
void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks;
void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback);

ビルドと実行の分割

ローカルマシンに接続されていないターゲットデバイスでテストを実行する必要があるときは、ビルドプロセスと実行プロセスを分割すると便利です。たとえば、ターゲットデバイスがクラウド内にある(またはクラウド内の複数のデバイスである)場合などがあります。 

これを実行するには、まずプレイヤービルドのテストプロセス自体をカスタマイズする必要があります。その方法を紹介します。 

  • プレイヤーがビルドされてすぐにテストが起動して実行されないように、AutoRun を無効にします。 
  • (デフォルトで保存される)システムの一時フォルダーではなく、既知の場所に保存します。 

次に、(コールバックインターフェースを使用して)プレイヤー側にカスタム結果レポートを追加し、すべての結果をキャプチャーして XML ファイルなどプロジェクトに対応する形式で保存します。 

ビルドと実行を分割する方法については、次のコード例を参考にしてください。Unite セッションでは、Richard Fine がこのアプリケーションの両方の部分(ビルドと結果レポート)のコードを順を追って説明しています(6:28 から)。

ビルドと実行の分割:ビルド

ビルド:

 

SplitBuildAndRun:build.cs (C#)
[assembly: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;
   }
}

ビルドと実行の分割:実行の結果を保存する

実行: 

SplitBuildAndRun:SaveResults.cs (C#)
[assembly:TestRunCallback(typeof(ResultSerializer))]
public class ResultSerializer : ITestRunCallback {
   public void RunStarted(ITest testsToRun) { }
   public void TestStarted(ITest test) { }
   public void TestFinished(ITestResult result) { }
   public void RunFinished(ITestResult testResults) {     
   	var path = Path.Combine(Application.persistentDataPath, "testresults.xml");
   	using (var xw = XmlWriter.Create(path, new XmlWriterSettings{Indent = true}))
       	testResults.ToXml(true).WriteTo(xw);
   	System.Console.WriteLine($"***\n\nTEST RESULTS WRITTEN TO\n\n\t{path}\n\n***");
   	Application.Quit(testResults.FailCount > 0 ? 1 : 0);
   }
}

メニュー項目から特定のテストを起動する

開発者が検証を記述すると、結果がコンソールウィンドウに表示されるものの、メッセージが絶え間なく流れる中で見失ってしまうことも少なくありません。検証結果をテストスイートで取得し、エディター内の専用の場所で明確に示されると便利です。これは、メニュー項目用の特定のテストを起動することで実現できます。  

まずは MenuItem に関連付けられたメソッドから始めます。他のエディター拡張メニュー項目で実行する方法とまったく同じです。そのメソッドにより、ScriptableObject コールバックオブジェクトが作成されます。正規のクラスの代わりに ScriptableObject を使用します。これにより、コールバックはそのまま維持しながら、テストがさまざまなこと(ドメインの再ロードを引き起こすなど)を実行できるようにします。ScriptableObject が有効になると、コールバックに登録されます。無効になると、登録が解除されます。 

次は、フィルターをセットアップします。MenuItem に対するテストを起動するときは、特定のカテゴリーやグループを対象にテストを実行する必要があることがよくあります。フィルターを使用すれば、テストを非同期的に実行できます。テストは複数のフレームにわたって実行され、テスト中に UnityTest、UnitySetUp、UnityTearDown によりエンジンループをティックするよう指定できます。テストが終了して RunFinished が登録されたら、メッセージを表示するか結果ウィンドウを開くように設定できます。ご自身のワークフローに合ったほうをお選びください。

以下のコード例をご覧ください。Richard が Unite セッションにてこのアプリケーションのコードのセットアップについて順を追って説明しています(11:50 から)。 

LaunchTestsFromMenuItem.cs (C#)
public class RunTestsFromMenu : ScriptableObject, ICallbacks {
   [MenuItem(“Tools/Run useful tests”)] public static void DoRunTests() {
   	CreateInstance<RunTestsFromMenu>().StartTestRun();
   }
   private void StartTestRun() {
   	hideFlags = HideFlags.HideAndDontSave;
   	CreateInstance<TestRunnerApi>().Execute(new ExecutionSettings {
       	filters = new [] { new Filter{ categoryNames = new[] { “UsefulTests” }}}
   	});
   }
   public void OnEnable() { CreateInstance<TestRunnerApi>().RegisterCallbacks(this); }
   public void OnDisable() { CreateInstance<TestRunnerApi>().UnregisterCallbacks(this); }
   /* ...RunStarted, TestStarted, TestFinished... */
   public void RunFinished(ITestResultAdaptor result) {       
   	…
   	DestroyImmediate(this);
   }

ビルドの前にテストを実行する

ビルドの前にテストを実行することはやり辛いと感じるかもしれません。ビルドプロセスではテストがコールバックから実行されることを必要としているため、エンジンの更新ループをくみ上げる機会がないからです。ただし、実際のビルドに時間をかける(プロジェクトによっては何分もかかることがある)前に、基本機能が機能していることを確認できることが利点です。 

このアプリケーションは、他の種類のビルドの前処理を実装する場合と同様に、IPreprocessBuildWithReport インターフェースを使用して実装できます。結果を取得するには、いつものようにコールバックを登録します。

ビルドの途中で再生モードに入ることはできないため、Test Runner API を使用して編集モードで特定のテストを実行できます。これらのテストは、ビルド前検証テストのカテゴリなど、カテゴリ別にフィルタリングすることで選択できます。これらのテストは同期的に実行できます。 

テストが終了したら、テスト結果を確認します。どこかでエラーが発生した場合は、BuildFailed 例外をスローすることでビルドプロセスを中止できます。 

このアプリケーションは、ResultCollector とプリプロセッサーの 2 つの部分に分けられます。このことについては、Richard によるこちらの講演にて詳細を説明しています(15:20 から)。 

Christian と Richard による Test Runner API に関するライブデモをご覧ください。セッション全体からさらに多くの QA に関するヒントが得られます。 

RunTestsBeforeBuild.cs (C#)
   public ITestResultAdaptor Result { get; private set; }     
   public void RunStarted(ITestAdaptor testsToRun) { }
   public void TestStarted(ITestAdaptor test) { }
   public void TestFinished(ITestResultAdaptor result) { }
   public void RunFinished(ITestResultAdaptor result)
   {
   	Result = result;
   }
}

このコンテンツにご満足いただけましたか?

はい。
いいえ。

弊社のウェブサイトは最善のユーザー体験をお届けするためにクッキーを使用しています。詳細については、クッキーポリシーのページをご覧ください。

OK