PlayMakerの処理をTestRunnerで自動テストする方法
PlayMakerで作成した処理のテストを行う際、シーンを手動で実行して目視で確認する方法では、工数が肥大化してしまいます。
そこで、本記事では Unity標準のTestRunnerを活用して、PlayMakerの自動テストを行う方法 を解説します。
これにより、手動での検証を減らし、効率的にバグを発見できるようになります。
本記事の想定読者
本記事は、以下のような方を対象としています:
- Unity TestRunner で C# のテストコードを書いたことがある
- PlayMaker を使ってSTYLY向けのシーンを作ったことがある
- PlayMakerの基本的な使い方を理解している
例題: タイム整形処理
本記事では、例題として以下の仕様の処理をテストしたいものとします。
-
入力:
float
型のレースタイム(秒) -
出力:
string
型の整形済みレースタイム(ミリ秒まで0埋めで表示) -
処理名:
TimeFormatter
具体的なテストケースとして以下を考えました。
入力 (float) | 期待する出力 (string) |
---|---|
3 |
3.000 |
3.123456 |
3.123 |
この先の説明をシンプルにするため、テストケースはこの2つのみとします。
PlayMaker側の処理を作る
1. 作業フォルダの作成
Assets/TimeFormatter
というフォルダを作成し、その中に TimeFormatterTest
というシーンを作成します。
2. TimeFormatter
オブジェクトの作成
シーン内に TimeFormatter
というGameObjectを作成し、PlayMakerFSMをアタッチします。
3. 変数の作成
Variablesに以下の2つの変数を作成します。
変数名 | 型 |
---|---|
InputNumber | Float |
OutputString | String |
4. ステートマシンの構築
以下の図のように、秒数を整形する処理を作成し、処理完了後に Done
ステートへ遷移するようにします。
今回は、 Convert Float To String
アクションの Format
に F3
を指定することで、小数第三位までの0埋め表示を実現します。
5. FSMの無効化
以下の図のように TimeFormatter
のPlayMakerFSMを無効化します。
これは、テストシーンを読み込み時にFSMが自動実行されるのを防ぐためです。
あくまでテストシーン専用の設定なので、プレハブを利用している場合、この設定をOverrideする必要はありません。
テストコードを作る
1. テスト用フォルダの作成
作業フォルダ配下に Tests
フォルダを作成し、その中に TimeFormatterTest.cs
を作成します。
2. テストコードの実装
using System;
using System.Collections;
using NUnit.Framework;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
namespace Segur.TimeFormatter.Tests
{
public class TimeFormatterTest
{
private const string SceneName = "TimeFormatterTest";
private const string GameObjectName = "TimeFormatter";
private PlayMakerFSM fsm;
[UnitySetUp]
private IEnumerator Setup()
{
// シーンを探す
var guids = AssetDatabase.FindAssets("t:scene " + SceneName);
if (guids.Length != 1)
{
throw new Exception("Scene name is not unique.");
}
// シーンを読み込む
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
EditorSceneManager.LoadSceneInPlayMode(path, new LoadSceneParameters(LoadSceneMode.Single));
yield return null;
// FSMを探す
var gameObject = GameObject.Find(GameObjectName);
fsm = gameObject.GetComponent<PlayMakerFSM>();
}
private IEnumerator RunTest(float inputNumber, string expectedOutput)
{
// 入力変数を設定
fsm.FsmVariables.FindFsmFloat("InputNumber").Value = inputNumber;
// FSMを実行
fsm.enabled = true;
// StateがDoneになるまで待機する。5秒でタイムアウト
var elapsedTime = 0f;
while (fsm.ActiveStateName != "Done" && elapsedTime < 5f)
{
yield return null;
elapsedTime += Time.deltaTime;
}
// Stateを検証。タイムアウトしていたら不合格になる
Assert.That(fsm.ActiveStateName, Is.EqualTo("Done"));
// 出力変数を検証
Assert.That(fsm.FsmVariables.FindFsmString("OutputString").RawValue, Is.EqualTo(expectedOutput));
}
[UnityTest]
public IEnumerator FormatsWholeNumberCorrectly()
{
yield return RunTest(3f, "3.000");
}
[UnityTest]
public IEnumerator FormatsDecimalNumberCorrectly()
{
yield return RunTest(3.12345f, "3.123");
}
}
}
このテストでは、PlayMakerの変数 (Variables
) を介してFSMを操作し、期待通りの出力になるかを検証しています。
PlayMode Test を有効にする設定
デフォルトでは asmdef
を作成しないと NUnit
の参照エラーが発生しますが、以下の設定を変更することでエラーを解消できます。
設定手順
ProjectSettings/ProjectSettings.asset
をテキストエディターで開き、以下の設定を 1
に変更してください。
playModeTestRunnerEnabled: 1
これにより、asmdef
なしでも PlayMode Test を実行できます。 1
テストを実行する
TestRunnerのPlayModeタブを開き、作成したテストを実行します。
すべてのテストが合格すれば、PlayMakerの処理が正しく動作していることが確認できます!
おつかれさまでした!
さいごに
本記事に掲載した手法は、 @kotauchisunsun 氏が考案したアイデアをもとにしています。
この場を借りて感謝いたします。ありがとうございました。
-
この設定をするとビルドした際にテストコードが含まれてしまう恐れがあります。ただ、今回の読者はSTYLYシーン制作者を想定しており、アプリビルドはしないため、問題はないと考えています。また、シンボル定義によってこの問題は回避できることもわかっています。 ↩