概要
TestRunner にて InputSystem を用いた入力処理のテストを InputTestFixture を継承したテストクラスを用いて行った際
個別実行したときには全てのテストが通過するにもかかわらず RunAll で実行すると最初のテスト以外すべて失敗するという現象にぶちあったのでメモ
構成
Unityバージョン : 6000.0.31.f1
TestFrameworkバージョン : 1.4.5
InputSystemバージョン : 1.11.2
現象の詳細
以下のコードのテスト、コード量が多くなるので二つのテストのみ
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEditor.SceneManagement;
namespace Game.Player.Tests
{
public class PlayerMoveTests : InputTestFixture
{
private Keyboard _keyboard;
[SetUp]
public override void Setup()
{
base.Setup();
if (_keyboard == null)
{
Debug.Log("初期設定");
_keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
}
}
[TearDown]
public override void TearDown()
{
base.TearDown();
}
[Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")]
[UnityTest]
public IEnumerator Test_PlayerMove_ArrowKeys_Correct()
{
yield return LoadTestScene();
yield return new WaitForSeconds(1f);
var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];
// 上下左右の入力通りに InputMove に値が入るか否か.
Press(_keyboard.upArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.up);
Release(_keyboard.upArrowKey);
InputSystem.Update();
Press(_keyboard.downArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.down);
Release(_keyboard.downArrowKey);
InputSystem.Update();
Press(_keyboard.leftArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.left);
Release(_keyboard.leftArrowKey);
InputSystem.Update();
Press(_keyboard.rightArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.right);
Release(_keyboard.rightArrowKey);
GameObject.DestroyImmediate(player.gameObject);
}
[Description("WASD キー入力で正常に InputMove に値が入るか.")]
[UnityTest]
public IEnumerator Test_PlayerMove_WASDKeys_Correct()
{
yield return LoadTestScene();
yield return new WaitForSeconds(1f);
var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];
// 上下左右の入力通りに InputMove に値が入るか否か.
Press(_keyboard.wKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.up);
Release(_keyboard.wKey);
InputSystem.Update();
Press(_keyboard.sKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.down);
Release(_keyboard.sKey);
InputSystem.Update();
Press(_keyboard.aKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.left);
Release(_keyboard.aKey);
InputSystem.Update();
Press(_keyboard.dKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.right);
Release(_keyboard.dKey);
GameObject.DestroyImmediate(player.gameObject);
}
// コード量が多くなるので省略.
private AsyncOperation LoadTestScene()
{
return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single));
}
}
}
このコードでテストを RunAll すると以下の画像のように先頭のテストのみ成功し、後ろのテストは全て失敗する現象が発生した
エラーコード
ArgumentOutOfRangeException: Cannot be negative
Parameter name: value
似たような不具合が発生していたようだがこちらでは解決していなかった
https://discussions.unity.com/t/error-when-running-all-unit-tests/886736/7
ただ、この中でシーン上にある PlayerInput を含むオブジェクトを削除したらうまくいくとの書き込みがあり試しにシーン上にあるオブジェクトを TearDown 時に削除するようにしてみた
試行錯誤 ( テストは失敗する )
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEditor.SceneManagement;
namespace Game.Player.Tests
{
public class PlayerMoveTests : InputTestFixture
{
private Keyboard _keyboard;
[SetUp]
public override void Setup()
{
base.Setup();
if (_keyboard == null)
{
_keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
}
}
[TearDown]
public override void TearDown()
{
CleanUp();
base.TearDown();
}
[Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")]
[UnityTest]
public IEnumerator Test_PlayerMove_ArrowKeys_Correct()
{
yield return LoadTestScene();
yield return new WaitForSeconds(1f);
var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];
// 上下左右の入力通りに InputMove に値が入るか否か.
Press(_keyboard.upArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.up);
Release(_keyboard.upArrowKey);
InputSystem.Update();
Press(_keyboard.downArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.down);
Release(_keyboard.downArrowKey);
InputSystem.Update();
Press(_keyboard.leftArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.left);
Release(_keyboard.leftArrowKey);
InputSystem.Update();
Press(_keyboard.rightArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.right);
Release(_keyboard.rightArrowKey);
}
[Description("WASD キー入力で正常に InputMove に値が入るか.")]
[UnityTest]
public IEnumerator Test_PlayerMove_WASDKeys_Correct()
{
yield return LoadTestScene();
yield return new WaitForSeconds(1f);
var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];
// 上下左右の入力通りに InputMove に値が入るか否か.
Press(_keyboard.wKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.up);
Release(_keyboard.wKey);
InputSystem.Update();
Press(_keyboard.sKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.down);
Release(_keyboard.sKey);
InputSystem.Update();
Press(_keyboard.aKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.left);
Release(_keyboard.aKey);
InputSystem.Update();
Press(_keyboard.dKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.right);
Release(_keyboard.dKey);
}
// 省略.
private AsyncOperation LoadTestScene()
{
return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single));
}
// シーン上にあるオブジェクトを全て破棄する.
private void CleanUp()
{
var activeScene = SceneManager.GetActiveScene();
foreach (var go in activeScene.GetRootGameObjects())
{
GameObject.DestroyImmediate(go);
}
}
}
}
このコードにしてもエラーが出てテストが失敗したのであきらめかけていたがエラーコードの内容が
ArgumentOutOfRangeException: Cannot be negative
Parameter name: value
から
System.ArgumentNullException : Value cannot be null.
Parameter name: source
に変わっていたので、シーン上にあるオブジェクトを削除したのが原因 -> _keyboard が null のままになってると推測を立てて
SetUp() 時に毎回 _keyboard に値を入れ直すようにしたところ RunAll でテストが全て通過するようになった
[SetUp]
public override void Setup()
{
base.Setup();
//if (_keyboard == null)
//{
_keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
//}
}
2回目以降は _keyboard == null で _keyboard 自体は null じゃなくて内部で利用している何かしらのインスタンスが null になってた?
ような挙動っぽい
解決したコード
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEditor.SceneManagement;
namespace Game.Player.Tests
{
public class PlayerMoveTests : InputTestFixture
{
private Keyboard _keyboard;
[SetUp]
public override void Setup()
{
base.Setup();
_keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
}
[TearDown]
public override void TearDown()
{
// TearDown前にシーン上のオブジェクトを全て破棄.
CleanUp();
base.TearDown();
}
[Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")]
[UnityTest]
public IEnumerator Test_PlayerMove_ArrowKeys_Correct()
{
yield return LoadTestScene();
yield return new WaitForSeconds(1f);
var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];
// 上下左右の入力通りに InputMove に値が入るか否か.
Press(_keyboard.upArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.up);
Release(_keyboard.upArrowKey);
InputSystem.Update();
Press(_keyboard.downArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.down);
Release(_keyboard.downArrowKey);
InputSystem.Update();
Press(_keyboard.leftArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.left);
Release(_keyboard.leftArrowKey);
InputSystem.Update();
Press(_keyboard.rightArrowKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.right);
Release(_keyboard.rightArrowKey);
}
[Description("WASD キー入力で正常に InputMove に値が入るか.")]
[UnityTest]
public IEnumerator Test_PlayerMove_WASDKeys_Correct()
{
yield return LoadTestScene();
yield return new WaitForSeconds(1f);
var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];
// 上下左右の入力通りに InputMove に値が入るか否か.
Press(_keyboard.wKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.up);
Release(_keyboard.wKey);
InputSystem.Update();
Press(_keyboard.sKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.down);
Release(_keyboard.sKey);
InputSystem.Update();
Press(_keyboard.aKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.left);
Release(_keyboard.aKey);
InputSystem.Update();
Press(_keyboard.dKey);
InputSystem.Update();
Assert.AreEqual(player.MoveInput, Vector2.right);
Release(_keyboard.dKey);
}
private AsyncOperation LoadTestScene()
{
return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single));
}
private void CleanUp()
{
var activeScene = SceneManager.GetActiveScene();
foreach (var go in activeScene.GetRootGameObjects())
{
GameObject.DestroyImmediate(go);
}
}
}
}
今後のバージョンアップでシーン上のオブジェクトを全削除しなくてもよくなってほしい...