Unity Test Runner(PlayMode)で[Scenes In Build]を使用しない方法4選
PlayModeでシーンのテストを行う場合、
[File] > [Build Settings] > [Add Open Scenes] > [Scenes In Build]
という手順でシーンを追加し、LoadSceneでシーンを呼び出してテストを行う方法があります。
しかし、この方法は以下の点が個人的に受け入れがたいため、別の方法を調査しまとめました。
- ビルドにテスト用のシーンが含まれてしまう。
- テストを作るたびにシーンを追加するのが手間。
0. 使用バージョン
- Unity 2021.3.4f1
- Test Framework 1.3.4
1. 前提
本記事では、Buildしてテストする場合(実機でのテスト)については想定していません。
具体的には以下の画像の左側で行うテストを想定しています。
また本記事では、ヒエラルキー上に複数のシーンが配置される状況を扱います。
詳しくは公式記事をご覧ください。
2. PlayModeで[Scenes In Build]を使用しない方法
以下、具体的な方法です。
2.1 自動で作られるシーン(InitTestScene)を使う
PlayModeで[Run All]等を実行すると、[InitTestSceneN...N]というシーン(N...NはDateTime.Now.Ticks
)が自動でロードされます。
このシーンを利用します。
後述の[UnityTearDown]処理の問題もあり、あまりお勧め出来ないように思います。
[UnitySetUp]等での準備は不要です。
例えばnew GameObject()
を行えば、InitTestSceneのシーンに追加されます。
[UnityTest]
public IEnumerator Test_1()
{
var scene = SceneManager.GetActiveScene();
Assert.That(scene.name, Does.StartWith("InitTestScene"));
Assert.That(scene.rootCount, Is.EqualTo(1));
var name = Guid.NewGuid().ToString();
var go = new GameObject(name);
Assert.That(GameObject.Find(name), Is.EqualTo(go));
Assert.That(scene.rootCount, Is.EqualTo(2));
yield break;
}
2.1.1 [Code-based tests runner]を消してはいけない
追加したGameObjectなどは[UnityTearDown]で削除することで、InitTestSceneのシーンを再利用してテストを続けることが可能です。
しかし、このInitTestSceneのシーンには最初から[Code-based tests runner]というGameObjectが存在しています。
このGameObjectはPlayModeでのテストに必要なオブジェクトなので、削除を行ってはいけません。
したがって、InitTestSceneのシーンを再利用する場合は、以下のようにするとよいでしょう。
なお、このような削除処理を行わない場合、テストで追加したGameObjectは次のテストでもシーン上に残ったままとなります。
[UnityTearDown]
public IEnumerator TearDown()
{
var scene = SceneManager.GetActiveScene();
foreach (var go in scene.GetRootGameObjects())
{
if (go.name == "Code-based tests runner") continue;
GameObject.Destroy(go);
}
yield break;
}
2.1.2 Assets直下に残っていたら消す
余談ですが、このInitTestSceneというシーンは、コンパイルエラーなどでテストが止まるとAssets直下に残ります。
残っていた場合は削除してしまってよいでしょう。
2.2 空のシーンを作って使う
SceneManager.CreateScene()を使用します。
シーンファイルを作るまでもない、ちょっとした物理挙動などを確認したい場合に便利です。
[UnitySetUp]で準備する場合は以下のようにするのがよいでしょう。
[UnitySetUp]
public IEnumerator Setup()
{
scene = SceneManager.CreateScene(
"TestScene",
//Guid.NewGuid().ToString(),
new CreateSceneParameters(
LocalPhysicsMode.None
)
);
SceneManager.SetActiveScene(scene);
yield break;
}
CreateScene
にてテスト中のヒエラルキーに[TestScene]というシーンが追加されます。
しかしこのヒエラルキーには既にActiveなInitTestSceneシーンがあるので、SetActiveScene
にて追加したシーンをActiveにします。
例えばnew GameObject()
はActiveなシーンに追加されるので、Activeにしておく方が便利です。
シーンを追加しているので、テスト後はシーンをUnloadする必要があります。
[UnityTearDown]でUnloadする場合は以下のようにするとよいでしょう。
[UnityTearDown]
public IEnumerator TearDown()
{
SceneManager.UnloadSceneAsync(scene);
yield return null;
}
UnloadSceneAsync
は非同期なので、yield return
で1フレーム挟んでおくとよいでしょう。
これによりシーンが削除され、次のテストのCreateSceneで名前が重複しなくなります。
名前の重複を避けるためにシーン名をGUIDにしてもよいでしょう。
2.3 既存シーンを開いて使う(Single Scene)
EditorSceneManager.LoadSceneInPlayMode()を使用します。
シーンファイルを作ってテストできるので、複雑なテストに対応できるのが利点です。
[UnitySetUp]で準備する場合は以下のようにするとよいでしょう。
[UnitySetUp]
public IEnumerator Setup()
{
scene = EditorSceneManager.LoadSceneInPlayMode(
"Assets/Path/To/MyTestScene.unity",
new LoadSceneParameters(LoadSceneMode.Single)
);
yield break;
}
LoadSceneMode.Single
を指定しているので、ヒエラルキーの既存シーンは全て閉じられるので注意が必要です。
自動で作られるInitTestSceneシーンも閉じられますが、テスト自体には影響ないようです。
この方法でテストを行った場合、テスト後にはテストを終了したシーンがそのまま残ります。
2.4 既存シーンを開いて使う(Multi Scene)
前項と同じくEditorSceneManager.LoadSceneInPlayMode()を使用します。
[UnitySetUp]で準備する場合は以下のようにするとよいでしょう。
[UnitySetUp]
public IEnumerator Setup()
{
scene = EditorSceneManager.LoadSceneInPlayMode(
"Assets/Path/To/MyTestScene.unity",
new LoadSceneParameters(LoadSceneMode.Additive)
);
yield return null;
SceneManager.SetActiveScene(scene);
yield break;
}
注意点はyield return null
で1フレームおいてからSetActiveScene
する事です。
同フレームでActiveにしようとするとLoadされていないという旨のエラーとなります。
この場合は、自動で作られるInitTestSceneシーンが残っています。
したがって、テスト後にテスト用のシーンをUnloadできます。
[UnityTearDown]でUnloadする場合は以下のようにするとよいでしょう。
[UnityTearDown]
public IEnumerator TearDown()
{
SceneManager.UnloadSceneAsync(scene);
yield break;
}
ヒエラルキーに同じシーンを複数Loadすることは可能なので、yield return null
で1フレーム入れる必要はありません。
しかし、1フレーム入れた方がきれいではあるでしょう。
3. 感想
個人的には「2.2 空のシーンを作って使う」と「2.4 既存シーンを開いて使う(Multi Scene)」がいいと感じています。
理由は、両方ともTearDownでUnloadSceneAsyncを使用できるので、同じTextFixture内でもテストを共存させられるためです。
それでは良きTest Runner(PlayMode)ライフを!
Discussion